News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_Devlin

Temple of Apshai translation project

Started by Devlin, 21:30, 12 December 24

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Devlin

I picked another game to try and translate - La Trilogie du Temple D'Apshai - It's going well so far, but i've run into a couple issues I can't really deal with.

the first is controls - I don't have any idea how to change the controls (f.ex. changing the o/n key inputs to y/n) and the second is that there's a bug with all CPC versions (including the original disc version) that causes loaded adventures (by way of ctrl-s during gameplay) to break the entire game - I'd like to figure out how I can fix that so it can be applied to all versions of the game for cpc.

I want to learn but don't know where to start using debuggers and emulator tools to make these kinds of changes.
CPC464 & CPC6128 + USIfAC II + Revaldinho 512k(universal cpld ver) - Schneider CRT TV
Administrator of Amstrad Discord : https://discord.gg/ksWvApv

Jean-Marie

This one looks straightforward, as it uses the Firmware functions to read the keyboard entries.
&BB1E   KM TEST KEY
      Action: Tests if a  particular  key  (or  joystick direction or
              button) is pressed
      Entry:  A contains the key/joystick nurnber
      Exit:   If the requested key  is  pressed,  then Zero is false;
              otherwise Zero is true for  both,  Carry is false A and
              HL are corrupt.  C holds  the  Sbift and Control status
              and others are preserved
      Notes:  After calling this, C will hold  the state of shift and
              control - if bit 7 is set then Control was pressed, and
              if bit 5 is set then Shift was pressed

&BB1B   KM READ KEY
      Action: Tests whether a key is available from the keyboard
      Entry:  No entry conditions
      Exit:   If a key  is  available,  then  Carry  is  true,  and A
              contains the character; otherwise Carry is false, and A
              is corrupt; in  either  case,  the  other registers are
              preserved
      Notes:  Any expansion tokens are not expanded

Most of the time, it's more difficult cause many games directly read the PPI ports to scan the keyboard.
Anyway, the O/N prompt can be seen at this offset :
org &0ae4
l0ae4:
ld a,34
call &bb1e            ;;test O pressed
jr nz,&0af3
ld a,46
call &bb1e            ;;test N pressed
ret nz
jr l0ae4
Here, I just replaced the Scan Code number 34 with 43(Y).

The loop processing a keypress can be seen here :
org &18d9
call &bb1b         ;;KM READ KEY
ret nc
call &12f5          ;;set window boundaries & clear window
call &22a3
cp &f0                 ;;test cursor up
jp z,&199d
cp &f1                 ;;test cursor down
jp z,&1a8d
cp &f2                 ;;test cursor left
jp z,&1b03
cp &f3                 ;;test cursor right
jp z,&1a10
cp &5a                ;;test Z
jp z,&1bb8
cp &58                ;;test X
jp z,&1bb8
cp &41                ;;test if lower than A
jp c,l194b
and &5f
cp &55                ;;test U
jp nc,l1973
cp &41                ;;test A
jp z,&1bb8
cp &43                ;;test C
jp z,&1bb8
cp &44                ;;test D
jp z,&1c88
cp &46                ;;test F
jp z,&1cbb
cp &4d                ;;test M
jp z,&1c99
cp &50                ;;test P
jp z,&2f46
cp &4f                 ;;test O
jp z,&2d14
cp &54                ;;test T
jp z,&1ee3
cp &52                ;;test R
jp z,&203d
cp &42                ;;test B
jp z,&20d3
cp &45                ;;test E
jp z,&2114
cp &49                ;;test I
jp z,&215c
jr l1973
l194b:
cp &0b               ;;test joy up
jp z,&199d
cp &0a               ;;test joy down
jp z,&1a8d
cp &09               ;;test joy right
jp z,&1a10
cp &08               ;;test joy left
jp z,&1b03
cp &13               ;;test CTRL+S
jp z,&198f
cp &20               ;;test Space
jp z,&1f2f
cp &2f                ;;test \
jp z,&20d1
cp &31               ;;test 1
jp z,&20d2
l1973:
ld hl,&46a7
call &0f4d
ret
So you may change them if you wish. Note that these are not Scan codes, but ASCII codes.

There's no bug with saving & loading, although it's a bit peculiar. When saving, you must give a 4 characters-long name to your file, for example TEST.
The thing is the code will prefix it with a "SAVE" string. Thus, the file on the disc will be named SAVETEST. That is the name you must use when loading it!

Devlin

okay, so I find the point where the game does input, change the scancode, easy enough.
but how do i then apply that to the file?
i imagine it's literally one byte in the hex editor to change, but how do i go about finding that one byte?
that's 18560 bytes to search through, after all.

Here's what I've gleaned understanding-wise from the save files, but it still breaks when loading an adventure - for whatever reason the game preserves the last filename used but appends anything typed in in subsequent saves - it sets a BASIC variable CH$ when you create any kind of save, but then doesn't go about filling that out as to say "here's the last filename used" - the load/save feature is all in BASIC, a quick hack could easily just blank that string and the length value PT and just force you to type the name in every time, though it's also entirely possible to actually make it work as intended and visualise the text properly.

When saving a character, (example name HERO) it creates a file called "HERO.PER"
however, when saving an adventure, it creates two files:
the first, "HERO.AV1" is a copy of the above character file.
the second, "HERO.AV2" is a copy of whichever dungeon you're in, a modified copy of the dungeon as loaded when the game loads a pieces file (pieces11.bin etc etc)

loading that adventure save for whatever reason seems to break the game's ability to load new levels at all, and on choosing another floor or dungeon will spawn you in where you're meant to start in that area, but on the floor you loaded your adventure from - 
for example, loading dungeon 1-2 from a loaded adventure on 1-1 will start you in a room just north of your actual starting point and so on.

CPC464 & CPC6128 + USIfAC II + Revaldinho 512k(universal cpld ver) - Schneider CRT TV
Administrator of Amstrad Discord : https://discord.gg/ksWvApv

Jean-Marie

Quote from: Devlin on 13:09, 13 December 24i imagine it's literally one byte in the hex editor to change, but how do i go about finding that one byte?
that's 18560 bytes to search through, after all.
There are 2 methods. You can indeed use an hex editor, search for the hexa string to be modified, and change the one relevant byte. In the present case, you'll fire up the debugger, go to offset 0AE4h, take note of the content of the hexadecimal values next to the ASM instructions. To wit, 3E 22 CE 1E BB 20 08 etc... Using the hex editor, you then search for that string (ctrl+F) and replace 22h (34) with 2Bh (43).
The other method, which I prefer, is to load the file to modify, set the cursor on the instruction to change (in that case ld a,&22), right-click, disassemble. The code will show up in the ASM editor, and you can change the instruction with ld a,&2B and reassemble the code. Finally, close the debugger, and save the file (save"TAT3",b,&800,18432).
Just like you would do with a Basic file : you'd open the file, edit & modify a line, and re-save it.
Quote from: Devlin on 13:09, 13 December 24loading that adventure save for whatever reason seems to break the game's ability to load new levels at all, and on choosing another floor or dungeon will spawn you in where you're meant to start in that area, but on the floor you loaded your adventure from - 
for example, loading dungeon 1-2 from a loaded adventure on 1-1 will start you in a room just north of your actual starting point and so on.
Damn, that looks complicated. I'll have to look into that.

Jean-Marie

I created a character (jeanmarie.per), and explored 4 levels. I made a save for each level (lvone, lvtwo, lvthree & lvfour). I've noticed a glitch where sometimes you can only type in a few characters when entering a filename. This is easily solved by pressing the DEL key before typing anything.
Other than that, it seems to be  working fine. I load the saved files by choosing A) Restore an ongoing game, then, when going back to the auberge, I can choose another level without any issue.🤔

Devlin

Quote from: Jean-Marie on 07:43, 14 December 24I created a character (jeanmarie.per), and explored 4 levels. I made a save for each level (lvone, lvtwo, lvthree & lvfour). I've noticed a glitch where sometimes you can only type in a few characters when entering a filename. This is easily solved by pressing the DEL key before typing anything.
Other than that, it seems to be  working fine. I load the saved files by choosing A) Restore an ongoing game, then, when going back to the auberge, I can choose another level without any issue.🤔
In that setup, load save lvfour, exit the dungeon and go back to the inn, then go to dungeon 1-1, and you will see what I mean by it's bugged out

So doing a bit of in-place palette fuzzing, I've noticed something. The game fails to load the dungeon data (it's likely using the load/save BASIC file to load the levels too) so instead of loading a new "pieces" file, it just uses what's already in memory with the new starting point information which breaks it. I'm going to look into this, and probably in the process end up debugging the load/save routine.
CPC464 & CPC6128 + USIfAC II + Revaldinho 512k(universal cpld ver) - Schneider CRT TV
Administrator of Amstrad Discord : https://discord.gg/ksWvApv

Jean-Marie

It's working fine with 464, but not 6128. Need to investigate the basic loader indeed!

Jean-Marie

I put a breakpoint on line 280. Loading the file returns the dreadful Memory Full error.
Basically, the code loads the whole file in Video RAM, and then copy a piece of it in the data area of the game. There should be a workaround hopefully😕

org &a480
ld bc,&0e40
ld hl,&c000
ld de,&9600
ldir
ret

Devlin

#8
Quote from: Jean-Marie on 02:21, 16 December 24I put a breakpoint on line 280. Loading the file returns the dreadful Memory Full error.
Basically, the code loads the whole file in Video RAM, and then copy a piece of it in the data area of the game. There should be a workaround hopefully😕

org &a480
ld bc,&0e40
ld hl,&c000
ld de,&9600
ldir
ret

That's about as much as I'd worked out, though minus the ASM stuff.
I've edited the tat4.bas file for now to exit to BASIC when it errors out so I can tinker and attempt to figure out a way to fix this - I'm much stronger with BASIC so I might possibly be able to work around it.

FIXED IT::: Added one line ~line "285 MEMORY &4000" which seems to have made it work?
Think the logic here is that I've basically told the interpreter "there's 16k memory free, use it" and it happily loads the next dungeon as intended - since printing himem gives you like 145 bytes "free" if you don't use that command.
CPC464 & CPC6128 + USIfAC II + Revaldinho 512k(universal cpld ver) - Schneider CRT TV
Administrator of Amstrad Discord : https://discord.gg/ksWvApv

Devlin

Quote from: Devlin on 07:43, 16 December 24
Quote from: Jean-Marie on 02:21, 16 December 24I put a breakpoint on line 280. Loading the file returns the dreadful Memory Full error.
Basically, the code loads the whole file in Video RAM, and then copy a piece of it in the data area of the game. There should be a workaround hopefully😕

org &a480
ld bc,&0e40
ld hl,&c000
ld de,&9600
ldir
ret

That's about as much as I'd worked out, though minus the ASM stuff.
I've edited the tat4.bas file for now to exit to BASIC when it errors out so I can tinker and attempt to figure out a way to fix this - I'm much stronger with BASIC so I might possibly be able to work around it.

FIXED IT::: Added one line ~line "285 MEMORY &4000" which seems to have made it work?
Think the logic here is that I've basically told the interpreter "there's 16k memory free, use it" and it happily loads the next dungeon as intended - since printing himem gives you like 145 bytes "free" if you don't use that command.
Probably should have tested this a bit more, seems to break other parts of the game in the process. I think it can be fixed but it might need more than just a hacky memory statement.
CPC464 & CPC6128 + USIfAC II + Revaldinho 512k(universal cpld ver) - Schneider CRT TV
Administrator of Amstrad Discord : https://discord.gg/ksWvApv

andycadley

The likelihood is that there isn't enough free memory for BASIC when a disk drive is attached - the MEMORY &4000 releases memory back to BASIC but then it'll trample over any M/code there.

There is probably a MEMORY statement in the BASIC already, probably just need to work out if that needs to be as low as it is currently set. If it does, the program might never be compatible with a disk drive without some recoding.

Jean-Marie

Memory &A67B seems to do the job. It needs to be tested thoroughly though.
It is the Himem value at startup. I added 2 lines : 285 & 295. I save & restore the current Himem value before & after loading the file. Not sure if really necessary, but better safe than sorry.
10 DATA 1,0,&e0,&21,0,&c0,&11,0,&96,&ed,&b0,&c9
20 FOR n=&A480 TO &A48B:READ a:POKE n,a:NEXT
30 CALL &803
40 ON ERROR GOTO 330
50 C1=1:C2=2:C3=3
60 A=PEEK(&4FFF):IF A<5 THEN ON A GOSUB 110,110,150,150 ELSE GOTO 270
70 GOSUB 320
80 CALL &800
90 GOTO 50
100 GOSUB 320:A=PEEK(&4FFF):IF A=1 OR A=3 THEN 30 ELSE 80
110 '
120 CLS:LOCATE 1,5:PEN C1:PRINT"PLACES TA DISQUETTE DANS LE LECTEUR":GOSUB 190:LOCATE 1,7:PEN C1:PRINT"ENTRES LE NOM DE TON PERSONNAGE:":GOSUB 220:GOSUB 230
130 LOCATE 1,10:PEN C1:IF A=1 THEN PRINT"LECTURE EN COURS":LOAD CH$+".PER" ELSE IF A=2 THEN PRINT"SAUVEGARDE EN COURS":SAVE CH$+".PER",B,PEEK(&4FFD)+256*PEEK(&4FFE),800
140 RETURN
150 '
160 CLS:LOCATE 1,5:PEN C1:PRINT"PLACES TA DISQUETTE DANS LE LECTEUR":GOSUB 190:LOCATE 1,7:PEN C1:PRINT"ENTRES LE NOM DE TON AVENTURE:":GOSUB 220:GOSUB 230
170 LOCATE 1,10:PEN C1:IF A=3 THEN PRINT"LECTURE EN COURS":LOAD CH$+".AV1":LOAD CH$+".AV2" ELSE IF A=4 THEN PRINT"SAUVEGARDE EN COURS":SAVE CH$+".AV1",B,PEEK(&4FFD)+256*PEEK(&4FFE),800:SAVE CH$+".AV2",B,&9600,&E40
180 RETURN
190 LOCATE 10,23:PEN C3:PRINT"APPUIES SUR UNE TOUCHE"
200 A$=INKEY$:IF A$="" THEN 200 ELSE LOCATE 10,23:PRINT SPACE$(22)
210 RETURN
220 LOCATE 1,8:PEN C2:PRINT"TAB POUR ABANDONNER":RETURN
230 '
240 LOCATE 32,7:PEN C1:PRINT SPC(8):LOCATE 32,7
250 A$=INKEY$:IF A$="" THEN 250
260 A1=ASC(UPPER$(A$)):IF A1=9 THEN RUN 100 ELSE IF A1=13 AND CH$<>"" THEN RETURN ELSE IF A1=&7F THEN CH$="":PT=0:GOTO 240 ELSE IF A1<65 OR A1>90 THEN 250 ELSE IF PT<8 THEN CH$=CH$+A$:PT=PT+1:PRINT A$;:GOTO 250 ELSE GOTO 250
270 '
280 INK 0,0:INK 1,0:INK 2,0:INK 3,0
285 H=HIMEM:MEMORY &A67B
290 LOAD"PIECES"+MID$(STR$(a),2),&C000
295 MEMORY H
300 POKE &A481,&40:POKE &A482,&E:CALL &A480
310 GOTO 80
320 LOCATE 1,17:PRINT"PLACES LA DISQUETTE DU JEU":GOSUB 190:RETURN
330 LOCATE 1,15:PRINT"ERREUR !":PRINT CHR$(7);:RESUME 100
 

Devlin

Quote from: Jean-Marie on 12:50, 16 December 24Memory &A67B seems to do the job. It needs to be tested thoroughly though.
It is the Himem value at startup. I added 2 lines : 285 & 295. I save & restore the current Himem value before & after loading the file. Not sure if really necessary, but better safe than sorry.
10 DATA 1,0,&e0,&21,0,&c0,&11,0,&96,&ed,&b0,&c9
20 FOR n=&A480 TO &A48B:READ a:POKE n,a:NEXT
30 CALL &803
40 ON ERROR GOTO 330
50 C1=1:C2=2:C3=3
60 A=PEEK(&4FFF):IF A<5 THEN ON A GOSUB 110,110,150,150 ELSE GOTO 270
70 GOSUB 320
80 CALL &800
90 GOTO 50
100 GOSUB 320:A=PEEK(&4FFF):IF A=1 OR A=3 THEN 30 ELSE 80
110 '
120 CLS:LOCATE 1,5:PEN C1:PRINT"PLACES TA DISQUETTE DANS LE LECTEUR":GOSUB 190:LOCATE 1,7:PEN C1:PRINT"ENTRES LE NOM DE TON PERSONNAGE:":GOSUB 220:GOSUB 230
130 LOCATE 1,10:PEN C1:IF A=1 THEN PRINT"LECTURE EN COURS":LOAD CH$+".PER" ELSE IF A=2 THEN PRINT"SAUVEGARDE EN COURS":SAVE CH$+".PER",B,PEEK(&4FFD)+256*PEEK(&4FFE),800
140 RETURN
150 '
160 CLS:LOCATE 1,5:PEN C1:PRINT"PLACES TA DISQUETTE DANS LE LECTEUR":GOSUB 190:LOCATE 1,7:PEN C1:PRINT"ENTRES LE NOM DE TON AVENTURE:":GOSUB 220:GOSUB 230
170 LOCATE 1,10:PEN C1:IF A=3 THEN PRINT"LECTURE EN COURS":LOAD CH$+".AV1":LOAD CH$+".AV2" ELSE IF A=4 THEN PRINT"SAUVEGARDE EN COURS":SAVE CH$+".AV1",B,PEEK(&4FFD)+256*PEEK(&4FFE),800:SAVE CH$+".AV2",B,&9600,&E40
180 RETURN
190 LOCATE 10,23:PEN C3:PRINT"APPUIES SUR UNE TOUCHE"
200 A$=INKEY$:IF A$="" THEN 200 ELSE LOCATE 10,23:PRINT SPACE$(22)
210 RETURN
220 LOCATE 1,8:PEN C2:PRINT"TAB POUR ABANDONNER":RETURN
230 '
240 LOCATE 32,7:PEN C1:PRINT SPC(8):LOCATE 32,7
250 A$=INKEY$:IF A$="" THEN 250
260 A1=ASC(UPPER$(A$)):IF A1=9 THEN RUN 100 ELSE IF A1=13 AND CH$<>"" THEN RETURN ELSE IF A1=&7F THEN CH$="":PT=0:GOTO 240 ELSE IF A1<65 OR A1>90 THEN 250 ELSE IF PT<8 THEN CH$=CH$+A$:PT=PT+1:PRINT A$;:GOTO 250 ELSE GOTO 250
270 '
280 INK 0,0:INK 1,0:INK 2,0:INK 3,0
285 H=HIMEM:MEMORY &A67B
290 LOAD"PIECES"+MID$(STR$(a),2),&C000
295 MEMORY H
300 POKE &A481,&40:POKE &A482,&E:CALL &A480
310 GOTO 80
320 LOCATE 1,17:PRINT"PLACES LA DISQUETTE DU JEU":GOSUB 190:RETURN
330 LOCATE 1,15:PRINT"ERREUR !":PRINT CHR$(7);:RESUME 100
 

Seems that it fixes the busted loading of new floors but then breaks character saves. Could it be possible to set the memory needed at start of file, then restore the "memory" on line 80, just before the game state is restored (CALL &800)
CPC464 & CPC6128 + USIfAC II + Revaldinho 512k(universal cpld ver) - Schneider CRT TV
Administrator of Amstrad Discord : https://discord.gg/ksWvApv

Jean-Marie

This seems to be working now. 

POKE &AC00,1
10 DATA 1,0,&e0,&21,0,&c0,&11,0,&96,&ed,&b0,&c9
20 FOR n=&A480 TO &A48B:READ a:POKE n,a:NEXT
30 CALL &803
40 ON ERROR GOTO 330
50 H=HIMEM
60 A=PEEK(&4FFF):IF A<5 THEN ON A GOSUB 110,110,150,150 ELSE GOTO 270
70 GOSUB 320
80 MEMORY H:CALL &800
90 GOTO 50
100 GOSUB 320:A=PEEK(&4FFF):IF A=1 OR A=3 THEN 30 ELSE 80
110 '
120 CLS:LOCATE 1,5:PEN 1:PRINT"PLACES TA DISQUETTE DANS LE LECTEUR":GOSUB 190:LOCATE 1,7:PEN 1:PRINT"ENTRES LE NOM DE TON PERSONNAGE:":GOSUB 220:GOSUB 230
130 LOCATE 1,10:PEN 1:IF A=1 THEN PRINT"LECTURE EN COURS":LOAD CH$+".PER" ELSE IF A=2 THEN PRINT"SAUVEGARDE EN COURS":MEMORY &A67B:SAVE CH$+".PER",B,PEEK(&4FFD)+256*PEEK(&4FFE),800
140 RETURN
150 '
160 CLS:LOCATE 1,5:PEN 1:PRINT"PLACES TA DISQUETTE DANS LE LECTEUR":GOSUB 190:LOCATE 1,7:PEN 1:PRINT"ENTRES LE NOM DE TON AVENTURE:":GOSUB 220:GOSUB 230
170 LOCATE 1,10:PEN 1:IF A=3 THEN PRINT"LECTURE EN COURS":LOAD CH$+".AV1":LOAD CH$+".AV2" ELSE IF A=4 THEN PRINT"SAUVEGARDE EN COURS":SAVE CH$+".AV1",B,PEEK(&4FFD)+256*PEEK(&4FFE),800:SAVE CH$+".AV2",B,&9600,&E40
180 RETURN
190 LOCATE 10,23:PEN 3:PRINT"APPUIES SUR UNE TOUCHE"
200 CALL &BB06:LOCATE 10,23:PRINT SPACE$(22)
210 RETURN
220 LOCATE 1,8:PEN 2:PRINT"TAB POUR ABANDONNER":RETURN
230 '
240 LOCATE 32,7:PEN 1:PRINT SPC(8):LOCATE 32,7
250 A$=INKEY$:IF A$="" THEN 250
260 A1=ASC(UPPER$(A$)):IF A1=9 THEN RUN 100 ELSE IF A1=13 AND CH$<>"" THEN RETURN ELSE IF A1=&7F THEN CH$="":PT=0:GOTO 240 ELSE IF A1<65 OR A1>90 THEN 250 ELSE IF PT<8 THEN CH$=CH$+A$:PT=PT+1:PRINT A$;:GOTO 250 ELSE GOTO 250
270 '
280 INK 0,0:INK 1,0:INK 2,0:INK 3,0
290 MEMORY &A67B:LOAD"PIECES"+MID$(STR$(a),2),&C000
300 POKE &A481,&40:POKE &A482,&E:CALL &A480
310 GOTO 80
320 LOCATE 1,17:PRINT"PLACES LA DISQUETTE DU JEU":GOSUB 190:RETURN
330 LOCATE 1,15:PRINT"ERREUR !":PRINT CHR$(7);:RESUME 100

andycadley

The fact it's doing a CALL &800 means that any value higher than MEMORY &7FF isn't going to be "safe" since you're essentially telling BASIC it can trample over the machine code with its own workspace.

If it won't work with that, then you've really not got much choice other than to disassemble the machine code and relocate sections such that anything important sits higher in RAM (things like temporary buffers that can be trashed during loading can be located lower of course).

Devlin

Quote from: andycadley on 10:14, 17 December 24The fact it's doing a CALL &800 means that any value higher than MEMORY &7FF isn't going to be "safe" since you're essentially telling BASIC it can trample over the machine code with its own workspace.

If it won't work with that, then you've really not got much choice other than to disassemble the machine code and relocate sections such that anything important sits higher in RAM (things like temporary buffers that can be trashed during loading can be located lower of course).
At this point, we've gotten it working without accidentally clobbering anything important - and that's good enough for me - these were meant to be hobby projects, as I really don't have the coding chops to re-implement huge swathes of code, and I wouldn't ask anyone to do that for the sake of something readily playable on other platforms.

For now I'm going to get the translations finished, and if I have the capacity, look into getting additional help to hack it further, but for now I want to finish translating it now I can test it properly.
CPC464 & CPC6128 + USIfAC II + Revaldinho 512k(universal cpld ver) - Schneider CRT TV
Administrator of Amstrad Discord : https://discord.gg/ksWvApv

Jean-Marie

#16
My main concern was that the BASIC code, which starts at 170h, would overwrite the machine code starting at 800h, but I checked and it's not the case. We would see the program crash instantly after the call &800 anyway.
Apart from that, the BASIC/AMSDOS have their own reserved Data area starting at A700h, and it's untouched by the machine code, so it should be good.
Still surprising that EPYX sold the game with such a bug.

Powered by SMFPacks Menu Editor Mod