Saturday, June 30, 2018

A newer CK5Patch fixes the %egadict issue

Only a day or so after releasing Abiathar v2.11.1, including among other things a workaround for CK5Patch's incorrect %egadict command, I was informed that there is a more direct fix. NY00123 released a new unofficial version of CKPatch a couple years ago that fixes CK5Patch itself. When I was originally embedding CKPatch, I went with the newest official version I could find, but I might have to consider updating to this new version.

Friday, June 29, 2018

Compressing images in an Excel workbook is very similar to in PowerPoint

Quite a while back, I devised a PowerShell script to convert all images in a PowerPoint presentation to JPG and compress them. More recently, someone needed to do the same thing, but for an Excel workbook. It turns out that XLSX archives have essentially the same structure as PPTX archives except that Excel-specific data is under the xl folder instead of the ppt folder for PowerPoint. Word DOCX archives use word, so the script should also work for them after tweaking the path.

Thursday, June 28, 2018

FMod - v2.11.1

Today I released the pending Abiathar changes as v2.11.1. That includes:

  • The Level Inspector's new switch-related checks
  • The Resource Accountant's new ability to show the number of each kind of item
  • The fix for the vertical lines in the selected tile crosshairs not scrolling properly
  • The workaround for CK5Patch's %egadict malfunction
Auto-update to v2.11.1 only works in v2.11 because of my hosting provider's recent security protocol change.

Tuesday, June 26, 2018

FMod - Level Inspector for switches

One new level creator had some trouble with switches in Keen 4. Unlike episode 6, episodes 4 and 5 have two different kinds of switches: one that affects the infoplane (to control moving platforms) and one that affects the foreground (to open/close bridges). Using the wrong kind of switch will produce absolutely no visible effect. To make navigating this issue easier for new modders, I added some new checks to the Level Inspector. It will now warn about pointerless switches, switches that point outside the level, and switches with the wrong kind of target.

Wednesday, June 20, 2018

Function-call-like parentheses in PowerShell cmdlet invocations will probably not do what you expect

One user tried to do something along these lines to call a function in PowerShell and pass the result as a parameter to a cmdlet:

Do-Thing -Information SomeFunc($abc)

This is an entirely reasonable thing to try, but PowerShell interprets it in a surprising way. First, it tries to be helpful by interpreting SomeFunc as a string literal. PowerShell function calls (distinct from calling .NET functions form PowerShell) don't use parentheses. In this case, PowerShell treats the parentheses as separating the $abc argument from SomeFunc, so the string "SomeFunc" becomes the value for the -Information switch and $abc, being unaffiliated with a switch, becomes a positional parameter. Usually this will result in error messages about a value not being suitable for any positional parameter.

To make PowerShell do what you likely wanted, wrap the entire function call in parentheses:

Do-Thing -Information (SomeFunc $abc)

Tuesday, June 19, 2018

Keen Modding Live - Untag permission adjustment

Today I realized it would be good for Keen Modding Live users to be able to remove tags they own from any levels, while still not being able to affix non-private tags to others' levels. This only matters for open tags that later close. I added a bit of logic to the "tag level" API to only do the level ownership check if trying to add the tag.

Monday, June 18, 2018

FMod - Resource Accountant improvement

One Abiathar user requested the ability to count how many of each kind of resource item are in the level, e.g. the number of 500-point items, in addition to how many total resources of each type there are. I implemented that today along with a new configuration option to control whether that new information is shown. The list is currently sorted by instances of the resource item kind, though that can look a little strange and might be changed.

Resource Account's results with item instance counts enabled

Sunday, June 17, 2018

FMod - Keen 5 %egadict fix

Today I adjusted Abiathar's patch generator to use a %patchfile command instead of %egadict for Keen 5. It would have been extremely easy and barely worth mentioning if done when the patch generator was first being written, but the patch generator configuration is in project files, so changes to it involve some fiddling. Specifically, the existing templates file and any given project file need to have the new fields' values copied into them from the appropriate fresh template.

Unrelatedly, I noticed that the vertical lines in the selected tile crosshairs don't scroll with the tileset. I adjusted the vertical line rendering to consider the tileset's Y offset and the crosshairs now render correctly.

Saturday, June 16, 2018

Need to adjust for faulty %egadict command

A user recently posted to Keen Modding about some trouble importing modified Keen 5 graphics with KeenGraph. KeenGraph's author showed up and determined that the problem is that CK5Patch's %egadict command (used to patch in KeenGraph's updated EGADICT) patches the file in at the wrong address. This issue can be worked around by manually specifying the address with %patchfile. I'll need to adjust Abiathar's patch generator to support special-case replacements for %egadict so that it can automatically write working patches for Keen 5.

Friday, June 15, 2018

SprintDLL - Arrays are possible now

Yesterday's improvements to SprintDLL's copyslot command made it possible to allocate chunks of memory and access them like arrays using constant indexes. That's very nice, but it would be even more helpful if the index could be determined at runtime from a slot's value. So today I made the pointer adjustment phrases and length switches accept either a constant number or a slot name. Therefore, things like this work:

allocslot native myInts: 4 sizesof int
newslot int toStore = 3
copyslot myInts dereferenced offset 1 sizesof int = toStore
newslot int index = 1
newslot int retrieve = 0
copyslot retrieve = myInts dereferenced offset index sizesof int
readslot /raw retrieve
// Prints 3

Thursday, June 14, 2018

Not all context menu items are directly in the Registry

One user wanted to find the command line equivalent of context menu items. This is straightforward for items that are configured in the Registry to launch a process with arguments (in the command subkey under a subkey of shell, which is a subkey of a file type key in HKEY_CLASSES_ROOT). However, some context menu items are actually created at runtime by shell extensions or the shell itself. Context-menu-affecting shell extensions are listed in the ContextMenuHandlers subkey under shellex. Of course, those are COM objects that can do anything they want in response to the menu item being clicked, so there's no simple way to invoke their functionality from the command line. One strategy is to use Process Explorer to see the command line of any new processes that result from clicking the item.

Wednesday, June 13, 2018

SprintDLL - More powerful copyslot

Today I finished the improvements to SprintDLL's copyslot command. It can now handle multiple qualifiers on a slot. Things along these lines now work, for example:

newslot int a
newslot block b = blockptr(blockptr(int 1))
copyslot a = b dereferenced dereferenced
readslot /raw a
// Prints "1"

When the appropriate copy region length can't be determined, a length switch is required, which can be /bytes (to set the length in bytes), /length (to specify the length with a unit), or /lengthof (to specify the kind of the value, especially useful for native).

Tuesday, June 12, 2018

SprintDLL - Starting copyslot improvements

SprintDLL's copyslot command supports some offsets and pointer derefererencing, but not combinations of those. Therefore, it always takes multiple instructions to get the value pointed to by a field, and it's literally impossible to deal with arrays. So today I started working on improvements to the instruction to allow things like someSlot field 5 dereferenced as a source or destination. I have a function to parse multiple qualifiers, but there's a snag: it's not possible for SprintDLL to know the kind of value at such an adjusted location, so it can't know how much to copy. Therefore I'm going to need to introduce a switch on copyslot to allow the user to specify the size of the destination region.

I also discovered a bug in the existing implementation: specifying qualifiers for the destination but not the source causes the destination's qualifiers to be ignored because of an ill-conceived optimization. The new implementation will fix that.

There's now a predefined zero slot that, unsurprisingly, holds a bunch of zero bytes. It's suitable for zeroing out a region by serving as a source slot in copyslot.

Monday, June 11, 2018

SprintDLL - Linebreak escape characters

I realized today that it was virtually impossible to get a linebreak into a SprintDLL string. A literal linebreak in the command would be interpreted as the end of the instruction and would cause a syntax error. Passing a blockptr to a collection of short literals (for character code points) in place of an lpwstr works, but is of course supremely inconvenient. So today I adjusted the parser to recognize a few more escape sequences in quoted strings. \r and \n now become a carriage return and line feed, as appropriate, and \N becomes a CR/LF sequence.
call kernel32.dll!GetStdHandle /return native /into out (int -11)
call kernel32.dll!WriteConsoleW (slotdata out, lpwstr "Hello world!\N", int 14, nullptr, nullptr)

Sunday, June 10, 2018

PowerShell aliases can't include parameters

I recently helped a user who was having some trouble with PowerShell aliases. He was trying to define a handful of aliases, each passing a different parameter to a function. That was a problem because PowerShell aliases are just alternate names for commands; they cannot specify parameters. (That's why, for example, mkdir is a function that has to do some fancy stuff to wrap New-Item, specifying -ItemType Directory but passing the other parameters straight through.) To work around that, you can use Invoke-Expression to run commands that create simple functions to call the more general function.

iex "function $NewFuncName {SomeFunction $TheParameter}"

Friday, June 8, 2018

Different browsers may print on different numbers of pages

One of my programs generates paper forms as HTML documents and brings up a browser for the user to print them. I had made sure the form would fit on one page, but one user was getting it printed on two. Upon investigating, I found that user's default browser was a moderately old version of Internet Explorer, which I had not tested on. Apparently not all browsers will size elements or the margin the same when printing, so even extremely simple arrangements should be tested on many browsers.

Wednesday, June 6, 2018

Keen Modding Live - Personal tags

I realized after implementing tagging yesterday that letting everyone affix arbitrary tags to everyone else's levels could conceivably go poorly. Attaching a normal tag now requires owning both the level and the tag; attaching an open tag still just requires owning the level. At creation, tags can be made "personal," in which case they can again be affixed by their owner to any level, but they will not be visible to any other non-admins.

Tuesday, June 5, 2018

Keen Modding Live - Tags

Today I made it possible to control whether a version newly published from Abiathar Live Studio becomes the official/suggested one. The "upload level" endpoint now has a parameter specifying whether to try to suggest the new version, which will only work if the version creator owns the level. When publishing a new version of an existing level, Abiathar Live Studio prompts for a version description and whether to try to suggest it.

For community level packs or compilations in general, it would be neat to have a way to tag levels. There are now API endpoints for creating, editing, and deleting tags, one to apply or remove a tag on a level, and one to get tag relationships by tag or level. Tag creators can mark the tags as open for submissions, in which case level owners can apply the tag to their own levels; otherwise, only the tag creator can apply the tag.

Monday, June 4, 2018

Keen Modding Live - Search for levels

Today I implemented the Search Levels dialog, which allows selecting a level to clone/fork. It downloads information on all levels, then allows filtering them by title, episode, and zone. Once a level is selected, the program prompts for the specific version.

I adjusted the Level Properties dialog to allow changing which version is the current/suggested/official one.

I added a database field in the level versions table to store a description, like a commit comment. Currently there isn't a way in the UI to set it, but if present it is displayed in the version selector.

Sunday, June 3, 2018

Keen Modding Live - Preload static data

A lot of information in Keen Modding Live won't change very often at all, like the list of zones or of episodes. Previously, Abiathar Live Studio would contact the API endpoint every time that information was needed. Today I adjusted it to download all the effectively-static information just once at logon/startup. This allows a couple dialogs like New Live Level to open immediately instead of after an API request.

Saturday, June 2, 2018

The downloadable Windows Update troubleshooter might work better than the built-in one

I recently tried to get a Windows 10 1709 VM to update to 1803. It consistently failed about 78% into the first installation phase. The Windows Update troubleshooter from Settings | Update & Security | Troubleshoot didn't seem to change anything. The equivalent troubleshooter downloadable from Microsoft, however, did seem to have an effect. The update still failed for me, but at least it got noticeably further into the process. (I'm still trying to figure out what issue it's running into.)

Friday, June 1, 2018

FMod - v2.11

After consulting with an Abiathar tester today, I published all recent changes as v2.11. I made one tiny tweak before finalizing it: to make sure it wouldn't crash on Windows XP (which supports only .NET Framework version 4.0), I made sure to handle any errors generated by the attempt to enable support for TLS 1.1 and 1.2. When running on .NET 4.0, the constants signifying those settings are not valid values for ServicePointManager.SecurityProtocol. The auto-updater will still not work on Windows XP under the current hosting setup, but at least it can make the attempt to contact the update server.

If an operation seems extremely common, check if it's done for you

I've been working with an application that uses an SQL Anywhere 11 database. Like hopefully all SQL implementations, SQL Anywhere supports autoincrementing columns. Instead of taking advantage of that, though, this application maintains a table of the next available primary key value for each table. To generate IDs for new rows, it calls upon a stored procedure.

The application has been around for a long time, and I'm sure if the authors were developing it today, they would make a better choice. I've certainly rigged up systems that were weirdly engineered or not necessary. Something I've learned is that very general, very useful functionality is probably already implemented in the platform. If it's implemented in a well developed, widely used piece of software, it is likely a solid solution. That's not always the case, but it's at least worth checking over. Before deciding to reimplement something already done in the platform, you should have a specific reason, like performance or a missing capability.