In the lower left of the disassembly window of IDA, there are three different indicators showing the current location. I'm currently browsing a Keen 5 disassembly and I see these values:
00008E2D | 0000622D: sub616D+C0
The leftmost value is the location in the EXE, the address you would give to your hex editor if you wanted to see that spot.
The one in the next section of the status bar is the address in memory, distinct from the EXE address because the EXE format has a header with various information before the actual code and data. This address is very important because it's the one you should give to CKPatch to alter the instructions in memory.
The last isn't really a distinct address; it's a restatement of the previous. It gives the current location relative to the last name or the current segment, depending on where you are in the file. In this case, I'm 0xC0 bytes into the function auto-named sub616D. If it's showing a segment (evidenced by the presence of another colon), you can use the Segmentation window to find the Base of the segment and use that to get an RL value for CKPatch. Otherwise, this alternate value display isn't super useful for patching purposes.
Various technical articles, IT-related tutorials, software information, and development journals
Friday, July 14, 2017
Thursday, July 13, 2017
FMod - Lone Editing and hidden planes
While working on my machine-assisted Keen 5 level pack, I noticed two things about Abiathar: the Lone Editing mode is fantastic, but also it has a tiny bug. Lone Editing makes sure only one plane is active at a time, so switching to editing only, say, the infoplane is a quick tap of the 3 key with no need to manually disable the previous plane first. Planes can also be hidden, which is a distinct state from being disabled/locked/visible. Sometimes I need to hide one plane to clearly see one behind it. Re-enabling a hidden plane, though, doesn't enforce the "one plane active at a time" rule. This confuses the paste preview plane, which proceeds to render extra planes in the translucent preview (but fortunately it places the correct ones).
The plane visibility toggle now considers Lone Editing mode. If there are any active planes when a hidden one's visibility is toggled, that plane becomes locked instead of active.
Additionally, I just now noticed a minor UI glitch. The "switch to tileset" method, which automatically updates plane states in Lone Editing mode, directly edited the plane state array instead of calling the appropriate function, so the checked state of the plane control menu items didn't get updated. That's fixed too.
The plane visibility toggle now considers Lone Editing mode. If there are any active planes when a hidden one's visibility is toggled, that plane becomes locked instead of active.
Additionally, I just now noticed a minor UI glitch. The "switch to tileset" method, which automatically updates plane states in Lone Editing mode, directly edited the plane state array instead of calling the appropriate function, so the checked state of the plane control menu items didn't get updated. That's fixed too.
Wednesday, July 12, 2017
Markeen level polishing project
Markeen is getting pretty decent at generating Keen 5 levels. As of yet, though, it hasn't actually been used for anything other than making pretty pictures for some people to look at. So I figured polishing up some Markeen-generated levels would be a good way to create my "own" levels without spending huge amounts of time building from nothing.
I started by profiling the default Keen 5 levels to a depth of 3, then using editprof noadj to discourage Markeen from placing certain obviously incompatible pairs of tiles next to each other. Then I used that profile to generate 100 starting points. Since Markeen is far from perfect, a lot of those levels are a mess, so I went through and deleted all the ones that would take too much time to salvage. That left me with 22 candidates. By default, Keen 5 only has 13 levels, so I could easily get away with making fewer than 22 of my own. One of my goals is to preserve as much of Markeen's "creativity" as possible, so I want to avoid having to place the fuse machines myself. Unfortunately, only 4 of the 5 were used anywhere in the 100 levels, but a couple of those 4 were used multiple times, so I'll probably use one of the levels containing an extra and just swap out the duplicate machine for the missing one.
So far, I've polished one level. It started like this:
And now looks like this:
With its linearity and lack of switches, doors, and keys, it's a good choice for a first level. This refinement only took about two hours including all the fine-tuning of the secret areas.
I started by profiling the default Keen 5 levels to a depth of 3, then using editprof noadj to discourage Markeen from placing certain obviously incompatible pairs of tiles next to each other. Then I used that profile to generate 100 starting points. Since Markeen is far from perfect, a lot of those levels are a mess, so I went through and deleted all the ones that would take too much time to salvage. That left me with 22 candidates. By default, Keen 5 only has 13 levels, so I could easily get away with making fewer than 22 of my own. One of my goals is to preserve as much of Markeen's "creativity" as possible, so I want to avoid having to place the fuse machines myself. Unfortunately, only 4 of the 5 were used anywhere in the 100 levels, but a couple of those 4 were used multiple times, so I'll probably use one of the levels containing an extra and just swap out the duplicate machine for the missing one.
So far, I've polished one level. It started like this:
And now looks like this:
With its linearity and lack of switches, doors, and keys, it's a good choice for a first level. This refinement only took about two hours including all the fine-tuning of the secret areas.
Tuesday, July 11, 2017
KeenGraph only checks for the old palette patch
Today I helped a PCKF member extract graphics from a Keen 1 mod that had an alternate color palette patched in. KeenGraph was able to extract the graphics, but it didn't process the palette patch, so the colors in the exported graphics didn't match the colors shown in the game. KeenGraph documentation claims that it can scan the patch file for an altered palette, so I was confused.
After trying various things (including changing the decimal values in the patch file to hexadecimal) I consulted the KeenWiki page on the palette patch. There, I learned that there are two versions of the palette patch, one old and one new. The only relevant different for patch file scanning purposes is that the new edition stores the color table in a different location. Apparently the version of KeenGraph I was using only knew about the old palette patch, and so didn't realize that the new version (used by this mod) affected the palette. Altering the patch file to use the old offset made KeenGraph use the right colors.
After trying various things (including changing the decimal values in the patch file to hexadecimal) I consulted the KeenWiki page on the palette patch. There, I learned that there are two versions of the palette patch, one old and one new. The only relevant different for patch file scanning purposes is that the new edition stores the color table in a different location. Apparently the version of KeenGraph I was using only knew about the old palette patch, and so didn't realize that the new version (used by this mod) affected the palette. Altering the patch file to use the old offset made KeenGraph use the right colors.
Monday, July 10, 2017
Abiathar API - Level sets
To provide an Abiathar level format, you have to implement INextGenLevelSet, from FMod.dll. This is made somewhat more tricky by the possibility of including tileinfo in some maps files, and by VeriMaps signatures. Level set implementations have these members:
- Levels is exactly what it claims to be: a dictionary of the levels in the level set, identified by their ID.
- HasTileinfo returns whether there is currently a tileinfo resource in the level set.
- TileSize is the side length of a tile in pixels. Abiathar only supports 16 pixels per tile at the moment.
- Tileinfo is the embedded tileinfo resource. If your level set format doesn't support tileinfo embedding, discard the provided object in this property's setter.
- IsVeriMapsSigned gets the VeriMaps status of the levels file. If your format doesn't handle VeriMaps, just return CantSign and do nothing in all the other VeriMaps-related members. NotSigned indicates that the set supports signing, but is not currently signed. Signed means that there is a signature and that Abiathar should try to verify it.
- Signer returns the username of the person who allegedly signed the levels file.
- SignedHash gets the signature on the level data that was stored when the file was saved.
- Hash gets the hash of the level data. You should use the Sha512 method in your loading method to compute this. It's up to you which parts of the file to authenticate.
- SigningCert is the certificate to use when saving a signed file. If this is set when your saving function is called, use the SignHash method to compute the signature. If this is null then, do not sign the levels.
Sunday, July 9, 2017
Abiathar API - Other event bus stops
There are three Abiathar event bus stops not previous covered in this API series.
AbiatharPlaneStateChangeEvent is sent when the user changes the state of a plane (e.g. from Active to Hidden). It includes the ID of the affected plane and the new plane state.
AbiatharSelTileChangeEvent is sent when the selected tile for a plane changes. This includes only the ID of the affected plane. You can get the new selected tile from the SelTiles array on the state manager.
AbiatharPatchGenerationEvent is sent when the auto-generated section of the patches is being written. To add patches, call AddLine for every line. Do not include the starting and ending commands; Abiathar handles these for you.
AbiatharPlaneStateChangeEvent is sent when the user changes the state of a plane (e.g. from Active to Hidden). It includes the ID of the affected plane and the new plane state.
AbiatharSelTileChangeEvent is sent when the selected tile for a plane changes. This includes only the ID of the affected plane. You can get the new selected tile from the SelTiles array on the state manager.
AbiatharPatchGenerationEvent is sent when the auto-generated section of the patches is being written. To add patches, call AddLine for every line. Do not include the starting and ending commands; Abiathar handles these for you.
Saturday, July 8, 2017
Abiathar API - Level formats
As of v2.9, Abiathar allows extensions to define level formats so that the editor can support other games. Implementing level formats is at a lower level than most extensions' work. To provide a format, an extension must have a reference to FMod.dll.
Abiathar internally uses an INextGenLevelSet to store the levels, so an extension that provides level formats must have a class that implements that interface. A level format object implements ILevelFormat, which has these members:
Abiathar internally uses an INextGenLevelSet to store the levels, so an extension that provides level formats must have a class that implements that interface. A level format object implements ILevelFormat, which has these members:
- ID gets the internal name of the level format. By convention, this should describe the storage format of the maps in some way. For example, the normal format is called Carmack.
- DisplayName gets the user-friendly name that shows up in the Level Format tab of the New Project Wizard.
- SizeRestrictions gets a LevelSizeRestrictions object describing the limits of the map format.
- WizardResources gets an array of up to three LevelResourceDescription objects. These objects set the file description (e.g. "GameMaps") and whether the setting refers to a real file (which is used to determine whether Browse should be enabled). If this is null, the level format uses a custom resource browser, and the NPW replaces the text fields with a single button to launch that browser.
- SupportedExtraData gets an array of extended data field IDs that the level objects support.
- LevelGenre gets a string that identifies the kind of level in some way. For example, "3PlaneKeen" is the genre for Keen Galaxy and Dreams.
- DisplayCustomLevelResourcesDialog instructs the format to display its custom resource browser. This will only be called if no wizard resources are supplied. The input and output arrays may contain up to three strings. Return null if the user cancels the dialog; return data if the user makes changes and saves them.
- CanContinue returns whether the NPW can advance given the current resource values. For plain file resources, Abiathar already checks for existence if necessary.
- SaveLevels saves the level set to disk using the resource configuration provided by the user.
- CreateLevels generates an empty level set.
- CreateLevel generates a blank level with the given dimensions. The return value may be a subclass of GalaxyLevel.
- LoadLevels loads a level set from disk.
- GetLevelExtraData returns an extended data field's value for the given level.
- SetLevelExtraData sets an extended data field's value for the given level.
To register a level format, call AddFormat on an AbiatharRegisterLevelFormatEvent in the event bus.
Subscribe to:
Posts (Atom)

