void allocate_36800_bytes(void)
reasonable functions that imply good things about how your program is designed
void allocate_36800_bytes(void)
reasonable functions that imply good things about how your program is designed
char * strlen(void)
I AM GOING TO TRY TO EXPLODE YOU WITH YOUR MIND
I would say it's the result of a badly written debug macro, ie there's a function that only prints #IFDEF DEBUG, instead of a function that's only called if DEBUG is defined.
except it takes no parameters, so that'd only work if it's a debug function that examples globals and prints about them.
which is possible, this program is global-heavy
I typo'd that as "global-heaven" which I guess is also technically true, in more way than one. Heaven is a big part of this game.
this program is either doing something sneaky and clever (overwriting its own code dynamically) or it's doing something stupid (calling a function with no body)
I pray (no pun intended) that it's the latter
I know programmers who use return values and parameters and they're all cowards
call it twice in a row! that makes even more sense!
sometimes the length of a string takes some time to settle, so to avoid race conditions you should always call strlen twice and check that the results match
also FUCK I wrote a 32bit x86 emulator! I need a 16bit emulator!
yes that's right, this isn't the first time I've gone "writing an x86 emulator would be easier than understanding this decompression code"
I hate decompression code THAT MUCH
it supports 20 entire instructions! (assuming you count Jcc as one, if not it's 25)
is that the same 25 instructions this code uses? NO!
11 years ago!?
maybe I should modify the emulator to give me Fake Protection. I need to write my own x86 emulator.
again. sigh.
I had the bad idea of using 16bit protected mode to help me extract the decompress function, but there's a lot of reasons why that is probably a very bad idea
I kinda want to yank the function out of the EXE into a separate tool and just call it remotely, but it's so globals-based that I'm worried it'd break writing some address I'm not emulating.
So being able to have the pages not loaded and watching for page faults would be useful! unfortunately, 16bit real mode
changing THE STACK POINTER? in a function called multiple times in the decompress?
WHAT THE FUCK ARE YOU DOING?
CLI
MOV SP ,word ptr CS :[DAT_1000_c780 ]
STI
ALRIGHT, FUCK YOU. I'M GOING TO BED
@foone Should be 10 per register, but make sure control register isn't in the dumb mode Windows defaults it to that breaks that.
anyone know off the top of their heads how many characters you can reliably store in your standard x87 floating point register?
"what if I just smuggled in filenames using floating registers?"
VERY NORMAL THOUGHTS, FOONE
this is a totally normal thing to think in 2025, guys.
trying to script the DOSBox-X debugger by making a keyboard probably counts as using my powers for evil.
I might still do it though. Someday I'll make a nice clean way to interface with DOSBox from python, but that would require a lot more work than just using a keyboard I made
the function that sneakily changes DS also changes SI.
fun.
this is definitely hand written assembly because compilers make more sense than this
I can't find the exact details but that's definitely what's happening
after it makes that array of 256 0xFFFFs, it makes another array that's the values 0-255.
suspicious. definitely doing some kind of huffmanning or LZ*
okay it's doing some weird effective-address calculating that means there's an implicit SS: prefix on that address.
Ghidra is not showing this to me, which is very very confusing
I'm now reading the DOSBox source code to try and figure this out because I have a normal life
it IS pulling from the SS register, but I do not know why
wikipedia has a great article documenting the ModR/M format used in x86 encoding, including a very handy table explaining how it works (in 16bit mode, too!), but it never mentions segment selectors once.
SS:BP+06 is in linear addresses, 27748h
but DS:BP+04 is 37758h
those are different places. this shouldn't work, unless I'm missing something very badly
16bit decompression is way worse than 32bit decompression, btw.
there's multiple different DS here and I'm not sure when it's changing. sometimes it's loading stuff from 4 and 8 and that can't be normal, those aren't real addresss
okay so, see the "mov ES, [bp+06]"? That's normal code that is getting the arguments to the function, right? which are at +4 and +6 on the original stack pointer. They're accessing through BP because the stack pointer changes when they push those other registers, to preserve them.
HOWEVER, look at the DS and SS. SS is 1810, but DS is 2811.
The problem is that that instruction should be accessing DS:bp+06, not SS:bp+04. and those are DIFFERNT
I would also like this to be less than the slowest thing possible, but unfortunately my experience with x86-on-python has shown that to be tricky.
what I want is a way to iteratively isolate a function.
Like I tell it "hey, grab these bytes from this EXE. that's a function with this calling function. now call it, but trap whenever it accesses global data I haven't defined or calls other functions"
then I can go through and add those extra functions until I have a magic Just Run The Function tool
in fairness, x86 is documented. I can look up how it works. I can't do that with this decompression
time for some late-night reading
it's definitely multi-image, rather than simply animated. For example, the puzzle pieces at the end of a run are all in one chunk, they're just different images inside it.
sadly this includes SODOMA.ART and SODOMB.ART, which render completely inaccurately.
but hopefully I will be able to fix this tomorrow
I did some looking into the file format. It's tricky because it doesn't seem to ever declare how many sub-images are in an image, it just determines it by the header length, and the header length is something like n*11+48?+16? and I'm only like 80% sure of those numbers.
I need to find the code in the binary for loading this. I've just been staring at the hex. it seemed simple enough, but the multi-image format is more complex than I thought
okay I think I've got all the non-animated images extracting properly.
other than one of jesuses which I can't figure out which palette it uses.
I think what's happening is that there's some animation supported in this format, and the extra animation bytes in the header are throwing off the parsing of the image (explaining the color and scrolling) and then I don't support the sprites yet (explaining the missing head)
I fixed the scrolling problem, but the palettes are still deeply fuckt.
WHERE'S YOUR HEAD?
whoops, I got my lefts and rights backwards. silly little-endian formats.
there are two ART formats: TITLEA.ART is the first one, and TITLEB.ART is the second one.
ewww. two halves of one image but they're encoded differently!
all dats extracted. I need to write some code to convert the ART files, but they're simple and I understand most of them already. but now I have 89 example files to work on.
IT WORKS! I have DAT2 now!
lets see how it breaks when we try DAT3...
my code starts right AFTER the chunk is loaded. I'd have to move it. MOTHERFUCKER
ALRIGHT HERE'S WHAT WE'RE DOING. WE'RE DOING IT LIVE.
loadChunk is gonna fail. Fine.
We ignore it, then call load_dat_file on the correct dat, then we call loadChunk again!
This is stupid, but it means I don't have to move the fucking start position!
yep, changing it to DAT2 breaks it. I can't swap DAT2 into the place of DAT1, because it does indeed need other chunks from it.
UGH.
maybe I can just add my own call to load_dat_file at the top of my assembly. I do NOT want to have to relocate it, that sounds like it'd be REALLY FUCKING HARD
I should do this to Carmen when I get back to that project. Then instead of understanding it's compression, I just bypass it completely
... they put BIB100.MUS in DAT1 and DAT2.
MOTHERFUCKER
I can't just extract them all to the same folder, because there's duplicates. possibly different duplicates!
ahh, I forgot the implicit state.
I'm injecting into the game when it first tries to load the title screen, which it does before it loads DAT2,DAT3,DAT5
hopefully I can just patch it to load DAT2 instead. That might break if it needs to use DAT1 resources first... I don't THINK it does? but I'm not sure.
TO THE DEBUGGER, BATMAN!
it worked successfully so of course as soon as I try it on DAT2, it starts hanging.
(that's why the filenames are a full 8.3 characters, so I can overwrite them with any valid DOS name)
then I compile that and I wrote a tiny python script to inject it into the EXE (by just copying the old EXE and pasting my shit in at an offset), and then I call that EXE once per chunk.
well, I call a dosbox-x exe with a conf set to just run it and then exit.
but since I didn't want to deal with writing code to parse the size and filenames and shit from the command line, I just have my script write a new EXE per chunk.
It just takes the old EXE, applies patches over where size, "internal.ext", and "12345678.ext" are in the EXE, then it runs that new EXE
so I hacked the game like this:
I wrote a little assembly program that basically just goes:
unsigned short size=64020;
ptr=loadChunk("internal.ext");
f=fopen("12345678.ext","wb");
fwrite(ptr,size,1,f);
fclose(f);
exit(0);
fuck it I'm overdumping every chunk by 5 bytes and I'll figure if I need to trim them later
YES! I extracted and decompressed every chunk in DAT1!
I can't tell if I'm 5 bytes off when writing out this file. That's REALLY annoying
ugh why am I even using fwrite. it's a pain when I'm writing assembly. I'm gonna give up and just call int 21h
normal thoughts for a python programmer:
"THIS C SHIT IS TOO HIGH LEVEL!"
I miss the days when I could write:
fwrite(ptr, size, 1, fp);
and not worry about what segment ptr is in
setting the ORG wrong in your x86 assembly really fucks it up. It was calculating the addresses all wrong!
fopen("w","1234568.ext")
WHY ISN'T IT WORKING?, I say, while hitting myself in the head
I ran into a minor problem because I had to rewrite some code, as I'd written "MOV DS,CS" which won't work: you can only move to segment registers like DS from general purpose registers.
So I rewrote it as:
MOV AX, CS
MOV CS, AX
Yeah that second line is a typo. I typed CS instead of DS. CS is where it gets instructions from, so the x86 will not let you write to it, although the encoding allows it. So yeah, that didn't work, it triggered a CPU fault
sadly I'm giving up on the floating point idea. I'm just gonna rewrite the EXE between every iteration. it's a bit easier.
PROTESTANT SPOTTED
(different religious traditions number the 10 commandments differently. The catholic 4th commandment is "Honour thy father and thy mother")
what is the theological use in teaching children to identify hellenistic-era settlements in the Levant?
no, angel, I will never give you up
oh hello there spooky mario 64 creepypasta, what are YOU doing here in this EGA bible game?
this explains why all the copies online are incorrectly cracked: They zipped up their installation, which worked fine... on their machine.
as soon as you run it on a different machine, it fails
It naturally encrypts the file. But it does it badly, so I can probably bypass it without too much trouble
And tested in DOS. It passes the copy protection!
okay, so I've patched out the copy protection, easy.
I've created the hacked disk, as 1.44mb, then created it again as the correct 720kb.
BUT IT TURNS OUT THERE'S ONE MORE
That "DAT4" file? It's actually supposed to be to let you install this game to a hard drive, and it works by building signature of what your computer looks like (or as well as it can in DOS), and comparing it to a file.
There we go.
Track 39, side B, and the sector numbers go 1,2,3,255,5,6,7,8,9.
No sector 4, so it'll fail to be found. Which is what I want.
I'm trying to format the disk with some RANDOM MEMORY as the sector numbers! no wonder it doesn't fucking work
I'm amazed I didn't notice that in the debugger!
OH WAIT I DON'T HAVE A DEBUGGER
FUCK! I clobbered ES! my code assumed ES never got modified, but when I had to do all the set-interrupt stuff I had to clobber it myself
save me foxflux:
https://dbalsom.github.io/fluxfox/index.html
HEY WHO STOLE MY WHOLE TRACK? WHERE ARE THE SECTORS?
BOY IT WOULD BE NICE IF I COULD OPEN THIS IN A FLUX VISUALIZER
I think that means I'm formatting the right track, but with the wrong sectors.
the one annoying thing about doing this in an accurate emulator is that it's accurately emulating how slow the floppy drive is
so my current format seems to work, but it leads to the whole track being bad, apparently.
sectors 02C7h through 02CF are bad.
okay I think the problem may be slightly weirder: norton disk edit told me the wrong format for this drive.
it's not 8 sectors a track, it's 9.
NOPE! int13h, AH=18 returns it in ES:DI and int 21h,AH=25h takes it in DS:DI
fortunately DOS has a simple syscall I can use to set the vector. And surely the int 13h,AH=18 returns the new vector in the same registers as the dos set-interrupt syscall, right?
RIGHT?
"Note: This function does not set the INT 1E vector to point at the returned parameter table; it is the caller's responsibility to do so "
whaaaat
so the notes for int 13h/AH=05h say to call int13h/AH=17h first, to set up int 1E
but the notes for Int 13h/AH=17h say not to use it, because it doesn't support 1.44mb drives, so use int 13h,AH=18h instead. but int13h, AH=18h doesn't set up INT 1E, which is THE ONLY REASON I'M CALLING IT
FAILED AGAIN.
somehow sectors 321h, 323h, and 329h are bad.
the fuck?
I'd love to show you what the low-level track looks like but I can't load .86f files the HxC tools. Once I do this on a real machine I'll have an image, though
BTW "BAD" is technically incorrect. Those sectors are not bad, in the sense we usually mean it: a spot on the disk that can no longer correctly store data, and reading from it will give checksum mismatches.
No, those sectors are just not there. The drive controller is on the track, reading for those sectors numbers, and it finds nothing that matches.
nope fucked up again
mov CX, 1300h ; TRACK 44
HEY FOONE, IS 44 IN HEX "13"?
track 44, side B, sector 3
okay I'm back to trying to do the floppy hack correctly (so, on a DD disk) and OH NO I have to figure out where the sector is again.
I hate converting between LBA and CHS
GNU social JP is a social network, courtesy of GNU social JP管理人. It runs on GNU social, version 2.0.2-dev, available under the GNU Affero General Public License.
All GNU social JP content and data are available under the Creative Commons Attribution 3.0 license.