Open Allofich opened 4 years ago
This is great. I can make you a collaborator so you can add this and anything else you like to the wiki.
Thanks. I've never edited a wiki before but I'll give it a shot later if you make me a collaborator. There's a few other things I've also found out that I can also add.
@afritz1, I've added a new page with the above.
I thought about where to put the spell points formula. I considered the existing pages but ended up making a new one called "Player and NPC stats".
I was thinking it may be good to move the NPCStats information from "Save File Formats" into "Player and NPC stats", and then just refer to NPCStats by name (maybe a URL link if that is possible) in the "Save File Formats" page, but for now I won't touch any of the existing text. @Carmina16, I welcome your input if you would like to say anything.
Also, things I add to the wiki will probably be a little bit inconsistent with the way Carmina16 wrote, in that I want to write pseudocode in C. Carmina16 put the offset locations of data as offsets from the start of the decompressed A.EXE (1.06) to the start of the data, whereas I was thinking of putting (as I did above) the offsets as they need to be entered in the aExeStrings.txt
and acdExeStrings.txt
files. For aExeStrings.txt
that seems to mean adding 0x3D30 to the "from-start-of-file" value, while for acdExeStrings.txt
that seems to mean adding 0x3F50 to the "from-start-of-file" value for decompressed ACD.EXE. (aftritz1, please correct me if I'm wrong about these values)
As for this issue, for now I think I will keep it open as a place for discussion about original game mechanics and the wiki, if that is all right with you afritz1.
Hmm, well it would be a pain to go back through all of the existing Wiki pages and make the offsets refer to the aExeStrings.txt
and acdExeStrings.txt
values, so I think i will just put the "from-start-of-file for decompressed A.EXE" value as Carmina16 was doing.
Changed the page to "Player stats" since NPC-specific stuff is already going in the "NPC" page.
Added information on spell and regeneration assignment to the NPC page.
Added information on the length of time that top-of-screen messages are shown to a new page called "Timing".
@afritz1, do you receive notification when I add to the Wiki? If so, I won't bother mentioning here when I've added something.
I get notices in my activity feed which is great. Thanks for adding all that stuff. I'm working on location refactoring right now but I will try getting to the things you added once I get that far.
The reason why the aExeStrings.txt offsets are different from Carmina16's is because the PKLITE decompression code I implemented is missing a chunk of data -- I think it's the original game's executable code. It wasn't needed since we only need to decompress the original executables for the global constant data.
I added lockpicking information, but unfortunately I don't know where the lockpicking difficulty of a door comes from yet. It's easiest to reverse the "end tips" of things that are closest to output, like code just before string messages that indicate success or failure. If you or Carmina16 know where door lockpicking difficulty comes from then you could probably get lockpicking running pretty quickly.
I get notices in my activity feed which is great.
OK, I won't post here just to say when I added something anymore, then.
I already have some code for lock difficulty. It comes from the level data in .MIF files. https://github.com/afritz1/OpenTESArena/blob/075ce256ccb4d446a7d1ebd90f7d8a2e33a12fb5/OpenTESArena/src/Assets/ArenaTypes.h#L36 https://github.com/afritz1/OpenTESArena/blob/94577cc802aa8a730aae187fd9dea2e83205e0ea/OpenTESArena/src/Assets/MIFFile.cpp#L313 https://github.com/afritz1/OpenTESArena/blob/9b885e1acda8da8c0a89bc8c2975e233f8873967/OpenTESArena/src/World/LevelData.cpp#L1056
You can add syntax highlighting to code blocks if you put the language right after the three back ticks. Like:
```C++
You can add syntax highlighting to code blocks if you put the language right after the three back ticks.
Thanks, I updated the pages and changed the data types like "byte" and "uint" to be primitive types like "unsigned char" and "unsigned int" so they will be highlighted as data types.
Some unsolved problems I have on my mind that I just want to write down and maybe looking in the original executable would help. I didn't see them in the wiki anywhere but maybe I missed them:
Interior raised platform texture coordinates (#129)
Wilderness raised platform sizes and heights (similar to #129)
Moon 1 and 2 positions (uses strange integer coordinates but I couldn't get it to look right yet. Ideally we could figure out what those numbers mean in regular Vector3 values)
Those various arrays are @4bf36
.
Sorry, I meant that I have the arrays but I don't know how to interpret them for all cases -- for interior raised platforms and wilderness raised platforms. They seem to be interpreted differently. Here is where they are used currently: https://github.com/afritz1/OpenTESArena/blob/2b0336f868a02f0c970eb4dd4bcf79ddeb93c7f2/OpenTESArena/src/World/LevelData.cpp#L690
I know that issue is for an interior, not the wilderness, and so this may not be useful, but does this snip from the original look to give the same results as what your code is doing?
Decompiled original in pseudocode:
if (worldType == WorldType::Wilderness)
{
for (int i = 0; i < 56; i++) // 56 entries covering Box 1A, 1B, 1C, 2A, 2B.
{
int value = BoxArrays[i] * 192;
BoxArrays[i] = value / 256 | (value / 512) * 256;
}
}
Looks useful but I don't immediately know how to work with BoxArrays
. Is int
16 or 32 bits in your example? I'll look at it more when I have time.
By BoxArrays
I mean the combination of the arrays of Box 1A, 1B, 1C, 2A and 2B, treating them as if they were all one array. They are all contiguous in the .EXE file.
The calculations done are 16-bit, so I guess that shouldn't be an int. I'll change it to an unsigned short.
Edit: The final value in Box1BWallHeightTable, 190h(400), will overflow beyond the 16-bit registers being used when multiplied by 192, so I wonder if it looks correct in the original game?
Thanks, will look at it after this current attempt with city entrance jingles.
Wait, sorry, it should be 32-bit after all. Changed it back to int.
Another similar bit of code exists dealing with the BoxArrays. I think while the above matches your !boxScale.has_value()
case (using 192), this one matches the boxScale.has_value()
case. But, it seems like while your code always uses a 32
here, maybe the original code can use anything over 31.
int a = funcCall() // This gets some value, it may be like your "inf.getCeiling().boxScale"
if (a < 33)
a = 100; // Don't know what this is
int b = -a; // Or this
int c = funcCall() // Same function as above. Gets the next value from the file?
if (c > 31) // I guess this value is like the "32" you use?
{
for (int i = 0; i < 56; i++) // 56 entries covering Box 1A, 1B, 1C, 2A, 2B.
{
int value = BoxArrays[i] * c;
BoxArrays[i] = value / 256 | (value / 512) * 256;
}
}
With your familiarity with the code, I'm hoping maybe some of these values will be recognizable to you. Or maybe Carmina16 knows more.
There is one other bit of code I can see regarding the BoxTables, which might be relevant to https://github.com/afritz1/OpenTESArena/issues/129. Like the above snippets, I don't know what this data represents, but maybe with your knowledge you can recognize things and piece it together.
if (*(unsigned char *)data != *(unsigned char *)(data + 1)) // If the next byte of some data (.INF or .MIF file?) being read differs from the current
{
int i = (*(unsigned int *)(data + 1) & 7); // Height index?
if (Player is outside) // Outside in city or in wilderness
{
if (Player is outside in city)
i += 8; // Use BoxB
else if (Player is outside in wilderness)
i += 16; // Use BoxC
}
short global1 = Box3AWallHeightTable[i] & 0x3f;
short global2 = Box1AWallHeightTable[i];
short global3 = -global2;
int index2 = *(unsigned int *)(data + 1) >> 3 & 0xf;
short global4 = Box4WallHeightTable[index2];
short global5 = 64 - (global4 + global1);
if (PlayerInWilderness)
index2 += 16; // Use BoxB
short global6 = Box2AWallHeightTable[index2] + global2;
short global7 = -global6;
short global8 = Unknown[(byte *)(data >> 4 & 0xf)]; // Unknown[] is an empty-at-start array.
unsigned char global9 = 1;
unsigned char global10 = 2;
short global11 = global7;
short global12 = global3;
}
The above pseudocode allows spilling over from one array to another. So index2 += 16
will cause Box2AWallHeightTable[index2]
, which is 16 elements, to actually read from Box2BWallHeightTable
.
The layout seems to be:
WORD dungeon_start[8];
WORD city_start[8];
WORD wilderness_start[8];
WORD interior_thickness[16];
WORD wilderness_thickness[16];
WORD source_copy[56]; // unscaled values of those above
WORD unknown1[8];
WORD unknown2[8];
BYTE unknown3[16];
There are only 2 unknown1
arrays, but the game addresses them as if there were 3, in the same order as the first 3 arrays.
The values from unknown1/2
and unknown3
are used as follows:
A <- unknownX[platform_start_id] mod 64
B <- 64-unknown3[platform_thik_id]-A
Those seems to be used in texture mapping of some sort. unknown3[...]
is probably the height of 64x64 texture in texels, and B
is the starting row of the texture.
Hello! I have stumbled into here quite late. But I am quite hopeful that I could help or learn a thing or too. I do not intend any actual coding work on this project, but I was hoping to view the assembly and reverse engineer the game. Provide any details about game mechanics and operations. I currently was intrigued how the magic defense system functions. Anyway... I recently installed "sourcerer" which is a decompiler for 16 bit programs that was used back in the day. Using that I was able to export dissasembled assembly code. But it is quite much for me. It is over 1000 lines of code and I have little idea where to start. I am only a college student and have had a little bit of progress with assembly using linux and various c programs. The dissasembled code lacks any functional call names or anything that can make it easy to read. For an idea of my assembly experience, I have worked with the well known "bomblab.c" that you can debug in GDB on linux. I'm in a course that covers assembly at the moment. I'm also quite eager to help though, I just have no idea where to start.
I saw people previously mentioned a decompressed A.EXE and I have found a website which seemed to reference strings they observed in the executable. I would also like to see this decompressed version. If anyone were to post a guide how to readily dissasemble the executable into something that proves easy to read along with any work, I think the collaborative effort would go much smoother and help the project move along!
I haven't invested time in disassemblers/tools like Ghidra, but if you want to play around with the decompressed A.EXE/ACD.EXE used by OpenTESArena, start with ExeUnpacker.cpp
and try writing this->exeData
out to a file. The VFS::Manager
code could probably be replaced with a simple fopen()
or something.
https://github.com/afritz1/OpenTESArena/blob/214998a15232b8535be0c72a69ffa6b676344c02/OpenTESArena/src/Assets/ExeUnpacker.cpp#L208
The project's HexPrinter
utility makes it easier to read.
https://github.com/afritz1/OpenTESArena/blob/214998a15232b8535be0c72a69ffa6b676344c02/components/utilities/HexPrinter.h#L9
I did a bomb lab too, lost a point cause I was a noob with breakpoints ;)
Thanks! (once with time again i wil delve into this, i may postpone work on this til winterbreak) I would hope Allofich might share how he derived some of the code formula for some of the game mechanics.
Also here is dissassembled assembly code of the unpacked ACD.EXE via sourcerer. ACD1-export.txt
I recently installed "sourcerer" which is a decompiler for 16 bit programs that was used back in the day.
You'll need an interactive disassembler like IDA or ghidra, and the debug version of Dosbox. Luckily, Arena is a real-mode application, so it would be easy to debug.
I would start with investigating strings and functions that use them, then identifying the game logic and data structures. You will also need the documentation on DOS, VGA and LIM/EMS interrupts and structures.
Added the pixelation effect to the wiki. https://github.com/afritz1/OpenTESArena/wiki/User-interface#pixelation-effect
Thanks. I always turned this effect off in Dosbox but will get to it sometime after 0.15.0.
It's also used in the intro screens (the ones with the scroll background) and it's always enabled there since you have to get in-game for the toggle to work, and starting a new game resets it to the default value.
Also, I didn't realize until I looked at the code, but the pixelation effect is done across the whole 320x200 screen, not just the message box areas. It isn't noticeable because the only difference in the screen pre-effect and post-effect is the message box being there or not being there, since everything else is paused.
I've reversed the game's formula for spell points. Of course, the results (the spell point multipliers per class, etc.) are already known information, so I guess this is mainly just for curiosity or for emulating the original game and using the .exe data.
I don't know whether this is also used for NPCs or if it's just the player.
The "SpellPointModifiers" array location for aExeStrings.txt is 0x40110. For acdExeStrings.txt it is 0x40750. It looks like: