ok, I tried to build the exes identified as v1.4, and it's indeed possible to byte-by-byte recreate the originals!
Note that if the patching utility is used, you should tell it to extract the data under KEEN4-6\static, and then run KEEN4-6\static\make.bat under a compatible DOS environment (e.g., DOSBox) in order to create the OBJ files for the linker, using Blzut3's makeobj program. These OBJ files should then be found under the separate KEEN4, KEEN5 and/or KEEN6 subdirs.
As expected from the readme, the recreated Keen 6 EGA v1.5 exe was more-or-less the same as the original, ignoring the referenced difference in CheckInTiles' layout and the workaround for it.
Maybe it'll remain unresolved, but it's really minor. I tried a few approaches to counter it without getting the desired result, unless a combination of these is maybe required. Details are purposefully somewhat-hidden with a different font color.
Examples I recall:
- Adding the hdrstop pragma.
- Redefining enum intiletype's values using separate macros.
- Changing the type of CheckInTiles' "intile" variable to intiletype or Sint16. In the latter case, I also tried to change the types of rowdelta and midx to Sint16. I still think that intile should be Uint16, due to its uses in the gamestate.keys array, and possibly also in the call to TileBonus (emphasis on the generated OBJ file's contents).
- Moving the definitions of a few global variables into CK_KEEN.C, in order to impact CK_KEEN.OBJ's layout.
The reason for the latter is that the function bodies are preceded by a list of extern functions/variables, which are used but not defined. Each referenced function appears to use 1 byte, while for a variable, the size in use is probably the variable's size minus 1.
I still haven't found a clear deterministic manner for determining if the compiler adds a byte of padding between a function body and a jump table or not. It does seem to be decided by the compiler, rather than the linker, thus it can be checked in the OBJ files. Such a byte seemed to get added in CK_TEXT.OBJ:_HandleCommand: and ID_US_2.OBJ:_USL_LoadCustom from what I recall, but (in the original exe) not in K6_ACT1.OBJ:_SpawnBlooglet.
I already encountered similar problems beforehand, and that's just with codebases built with Borland C++. A couple of examples I recall:
- The layout of at least one function in The Catacomb Armageddon v1.02/Apocalypse v1.01 wasn't fully matching, using the open-source release. I could get it to match in size by changing the order of addends in a sum IIRC, but it was still not 100% matching.
- For The Catacomb Abyss v1.24, you just had to change the value of STARTTILE8 in GFXE_ABS.EQU, but not in GFXE_ABS.H. I really don't know why was this the cause, other than using an outdated file, having an unnoticeable data corruption or going through a Borland C++ (assembler) bug. I'm quite sure this had no impact on the game due to the lack of 8x8 tiles, so no assembly drawing runtime depending on STARTTILE8 is used.
K1n9_Duk3 wrote: ↑Thu Jul 01, 2021 21:54
NY00123 wrote: ↑Thu Jul 01, 2021 20:43
Although I'm wondering how much problems there were with the orders of global variables in the exes. I think that there could maybe be more issues with this while using Borland C++ 2.0, but I'm unsure.
I was actually about to contact you and ask for some help or advice, but after browsing your recreation thread on the RGB Form once again and checking out the additional information in your bitbucket repository (and checking out disassemblies and the compiler settings of Keen Dreams and Catacomb 3-D), I was able to figure this out myself.
It's great to see that my information and posts were useful!
Fortunately, I only focussed on v1.4 of Keen 4-6 EGA (and also Keen 6 v1.5 EGA) and all of those were compiled wth Borland C++ 3.0 or 3.1, which appear to arrange the global variables in a way that's much easier to reverse engineer. Any earlier versions of Keen 4-6 were indeed compiled with Borland C++ 2.0, but I didn't bother trying to recreate exact copies of those executables.
So it is less of a problem with 3.0/3.1, as I suspected.
One may give the CGA versions a try now! (Just kidding...or am I?)
I did run into an issue when compiling the "Return to the Shadowlands" source code with Borland C++ 2.0 where the code compiled without issues, but the executable would always quit with an "Abnormal program termination" error. The compiler appears to have forced the "grneeded" array into the main data segment instead of giving it its own far data segment. That meant the main data segment didn't have enough free space for the stack and the environment (command line parameters and such).
That's a bit unexpected, given that grneeded is clearly defined as a part of far memory. Maybe it's a compiler bug or another related glitch; I recall having problems while using Borland C++ 3.0, where you could unexpectedly get compilations errors, but not in a consistent manner. Maybe it happened in my Wolf3D tree.