Hej!
To ten loader przy grze triad do jest wygenerowany przez Turbo Copy 3 lub Turbo Copy 4, autorstwa Roberta Kujdy. Była już walka z tego typu plikami w tym wątku: Turbo Copy 3/4 post i link do prymitywnego narzędzia które umożliwia analizowanie i dekodowanie tego rodzaju plików do postaci rozpakowanej.
Niby skopiować ten plik powinien dać radę również ten program: Turbo Copier 3/4, ale wychodzi na to że triad też jest dodatkowo XOR-owany, a więc trzeba by użyć np. a8cas-convert od krótkiego i TCX Tools do zamiany tego pliku ponownie na format Atari-DOS (xex).
Pewnie zapytasz czemu tym razem a8cas-util od krótkiego a nie a8cas-util od FUJI-ego? Zauważyłem że a8cas-util jakoś lepiej radzi sobie z takimi taśmami/plikami WAV z zapisem w standardzie (czyli modulacja FSK) to a8cas-util. Czemu tak jest? Nie wnikałem za bardzo, zapewne autorzy obu rozwiązań musieliby nam to wytłumaczyć. Tak jest również i w tym wypadku. a8cas-util nie radzi sobie z konwersją pliku WAV do CAS, występuje co najmniej jeden błędny rekord, a w przypadku użycia a8cas-convert problemu z konwersją nie ma.
Może zatem szybki poradnik jak odzyskiwać/konwertować pliki w tym formacie, postaram się to zapisać w formie kompaktowej:
1) dokonujemy konwersji pliku WAV -> HEX, za pomocą a8cas-convert:
a8cas-convert.exe -fh 01_Triad_normal_strona_A.wav 01_Triad_normal_strona_A.hex
2) dokonujemy ręcznej edycji powstałego pliku HEX, aby pozbyć się loadera i 6-bajtowego rekordu "zabezpieczającego", a więc początek oryginalnego pliku HEX wygląda tak:
A8CAS-HEX
FUJI
fsk 00087 6 115 4 22 1 6 20 5 5 10 12 13 4 6 9 6 5 4 15 11 1 46 2 39 1 6 2 167 2 11 1 3 3 10 2 9 1 8 2 ; length=39; duration=59 ms
baud 00588
data 19670 55 55 fc 00 06 00 07 2a 07 a2 00 a0 00 fe 2b 07 51 04 59 00 04 48 b9 00 04 99 00 0a 68 e8 c8 d0 ec 8d fc bf ee fc bf cd fc bf d0 02 38 60 a8 30 8c 2f 01 a8 08 8c 30 01 ac fb be 8c 29 08 a1 07 9f a8 1f a3 07 a1 fe 85 47 e7 8d c5 01 8d c7 01 8d 43 01 85 13 a4 13 c8 0f cf f9 e7 85 08 ac b7 08 c8 e8 cf 19 1f 0a 08 8c e0 01 a8 0c 8c df 01 1f 7e 06 1f a1 06 1f 95 07 1f 01 08 1f 0c 07 4b 73 e3 a1 25 ; standard record; length=132, checksum=25 OK
data 00270 55 55 fc 0f a8 02 9c 41 02 a8 03 9c 49 02 a8 24 9c 43 02 a8 08 9c 44 02 a8 7f 9c 4a 02 a8 11 8c fb 01 4b 55 e3 1f 3b 07 ac ee 01 cc 82 06 cf ce a4 13 c4 13 ef f9 1f 52 07 a4 42 8c 79 06 a4 43 8c 7a 06 9f ff 1f 0d 07 c8 be cf 0e 1f 0d 07 a9 a8 ff 1f eb 06 c9 cf f9 4b e8 06 c8 ce cf 09 1f 0d 07 a9 1f 0d 07 4b ce 06 1f eb 06 4b bf 06 9f ff 90 42 47 e5 42 cf 01 e5 43 a4 42 c4 44 a4 43 e4 45 8f 88 ; standard record; length=132, checksum=88 OK
data 00268 55 55 fc 0a 67 67 67 1f 52 07 2f 03 4b bf 06 67 5f 89 84 48 a1 0f a8 ff 9c 47 02 9c 48 02 a8 06 9c 41 02 1f 55 e3 2f 07 48 77 47 a4 48 a9 68 60 bd 43 03 c9 88 d0 0f 20 96 08 68 68 a0 80 60 a9 ed 48 a9 3c 48 60 a9 53 8d 3b 09 a9 09 8d 3c 09 20 96 08 4c 50 08 20 90 08 20 08 09 20 0e 08 85 43 20 0e 08 85 44 a9 ff c5 44 d0 04 c5 43 f0 e6 a9 fe 85 48 20 0e 08 85 45 c9 e3 d0 02 e6 48 20 0e 08 85 0d ; standard record; length=132, checksum=0d OK
data 00267 55 55 fc 46 c9 02 d0 02 e6 48 e6 45 d0 02 e6 46 a0 00 60 a0 3c 8c 02 d3 60 a2 10 a9 0c 9d 42 03 20 56 e4 a2 e4 a0 5f a9 06 4c 5c e4 ee c4 02 ad c4 02 8d 16 d0 ce 28 09 d0 11 a9 04 8d 28 09 ad c7 02 18 69 10 8d c7 02 8d 19 d0 46 4d ae 29 09 ca 10 29 ee 4e 09 d0 03 ee 4f 09 ad 4e 09 c9 81 d0 18 ad 4f 09 c9 0a d0 11 a9 8f 8d 4e 09 a9 09 8d 4f 09 bd 2b 08 c9 8f d0 f9 a2 07 8e 29 09 8e 04 d4 4c 71 ; standard record; length=132, checksum=71 OK
data 00270 55 55 fc 5f e4 6c e0 02 6c e2 02 20 05 09 a9 0d 8d e2 02 a9 08 8d e3 02 a6 48 30 0b a0 34 20 92 08 86 14 c4 14 d0 fc 60 43 3a 9b 04 07 ff 52 4b 31 39 38 37 70 70 70 70 70 70 70 70 70 46 67 09 70 70 70 70 70 47 7b 09 70 70 70 70 70 70 70 70 56 8f 09 41 31 09 00 00 00 00 00 ec ef e1 e4 00 e5 f2 f2 ef f2 00 00 00 00 00 00 00 00 00 00 00 00 ec ef e1 e4 e9 ee e7 00 00 00 00 00 00 00 00 00 00 00 4d ; standard record; length=132, checksum=4d OK
data 00271 55 55 fc 00 00 00 34 32 29 21 24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 0a 00 00 f2 ef e2 e5 f2 f4 00 61 6e 64 00 f0 e9 ef f4 f2 00 00 0a 0a 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 0a 00 00 f2 ef e2 e5 f2 f4 00 61 6e 64 00 f0 e9 ef f4 f2 00 00 0a 0a df ; standard record; length=132, checksum=df OK
data 00272 55 55 fc 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 0a 00 00 f2 ef e2 e5 f2 f4 00 61 6e 64 00 f0 e9 ef f4 f2 00 00 0a 0a 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 68 ; standard record; length=132, checksum=68 OK
fsk 15797 13 9 8 9 9 8 9 8 9 8 9 8 10 8 9 8 9 8 9 9 9 76 50 9 17 8 18 8 18 16 19 7 26 ; length=33; duration=45 ms
data 00358 55 55 fc 87 87 78 7e 31 7e 34 4b 7e 2a 2c 35 55 2b 17 1e 0c 0f 19 0a 1d 58 41 56 49 48 56 40 4b b7 7b 58 2b 9c 48 7b 34 0f 9c d1 2b f5 7a 7b 58 2b 9c d5 93 7a b1 e7 88 7b 34 0f 9c da 70 c5 38 7e e5 87 27 b2 a8 8f 34 a1 4f 30 f2 30 d1 78 f5 3e 69 78 98 7a 99 7a 78 7e 78 18 b8 c1 34 0f 9c 34 1c 9c 3e 69 d5 3e 69 96 3e 69 b5 3f 69 88 67 b7 7b 72 d2 58 78 6e d5 d3 75 35 d4 75 f5 d3 75 58 78 6e 59 ; standard record; length=132, checksum=59 OK
data 00270 55 55 fc d5 d3 75 35 d4 75 f5 d3 75 34 70 18 d1 78 f5 e7 62 d5 e7 62 96 e7 62 b5 d8 62 88 67 b7 7b 72 d2 58 dd 61 d5 d3 75 35 d4 75 f5 d3 75 58 dd 61 d5 d3 75 35 d4 75 f5 d3 75 34 4f 18 d1 78 f5 3f 69 f5 d8 62 58 a3 d7 d1 78 f5 d8 1b d1 79 f5 bf 0c d1 38 51 86 f5 b0 0c d1 1c f5 b1 0c d1 78 f5 b4 0c f5 b2 0c f5 b3 0c f5 c8 0d f5 c9 0d f5 10 0f f5 11 0f f5 12 0f d1 78 f5 06 0f d1 78 f5 fe 01 ee ; standard record; length=132, checksum=ee OK
data 00269 55 55 fc f5 ff 01 f5 f0 01 f5 f1 01 f5 bb 02 f5 3c 04 f5 3d 04 f5 30 04 f5 c3 05 f5 c4 05 f5 c5 05 f5 19 06 f5 1a 06 f5 1b 06 f5 80 07 f5 81 07 f5 7c f8 f5 7d f8 f5 5f f9 f5 50 f9 f5 82 07 f5 40 fa f5 db fb f5 dc fb f5 dd fb f5 84 fc f5 85 fc f5 86 fc f5 52 fe f5 53 fe f5 54 fe f5 56 0a 10 d2 10 18 30 e0 30 f2 30 d5 cb 0d 88 7b b6 cb 0d d5 12 0f 88 7b b6 12 0f d5 d8 1b a8 7b 34 b1 1a b1 64 dd ; standard record; length=132, checksum=dd OK
data 00269 55 55 fc a8 75 d1 78 f5 d8 1b d5 d8 1b 88 83 34 bc 1a b1 b7 79 c7 a8 68 d5 ba 1b b1 78 88 7e 58 6d 1c 34 74 1c 34 bc 1a b1 f9 a8 70 d1 78 f5 d9 1b 34 bc 1a b1 fd a8 70 d1 79 f5 d9 1b 34 bc 1a b1 c1 a8 73 d1 87 35 e5 1b f5 e5 1b 34 bc 1a b1 e8 a8 73 d1 87 35 e6 1b f5 e6 1b 34 bc 1a b1 ed a8 6b d1 87 35 e5 1b f5 e5 1b d1 87 35 e6 1b f5 e6 1b 34 bc 1a b1 f0 a8 3a d1 78 f5 bd 1b d5 ba 1b b1 78 e1 ; standard record; length=132, checksum=e1 OK
data 00287 55 55 fc 88 7e 58 6d 1c 34 77 1c d5 eb d1 fd d0 d5 ec d1 fd b7 7b d1 f5 e1 d1 58 4d 72 58 3d 72 d5 d3 75 35 d4 75 f5 d3 75 58 3d 72 d5 d3 75 35 d4 75 f5 d3 75 58 45 72 34 bc 1a b1 d5 a8 2c d1 79 f5 bd 1b d5 ba 1b b1 78 a8 c2 58 4d 72 d5 5a d0 fd d0 d5 5b d0 fd d1 58 3d 72 d5 d3 75 35 d4 75 f5 d3 75 58 3d 72 d5 d1 df fd d0 d5 d2 df fd d1 d1 c9 f5 d7 df 58 3d 72 d5 d3 75 35 d4 75 f5 d3 75 58 90 ; standard record; length=132, checksum=90 OK
A więc musimy się pozbyć śmieci z początku (pierwszy blok FSK), potem jest loader (6 rekordów w NORMAL-u) oraz rekord końca pliku, z następnie krótki 6-bajtowy rekord (tutaj a8cas-convert zrobił z tego blok FSK, ale to nie istotne usuwamy również ten rekord), ma pozostać tyle:
A8CAS-HEX
FUJI
data 00358 55 55 fc 87 87 78 7e 31 7e 34 4b 7e 2a 2c 35 55 2b 17 1e 0c 0f 19 0a 1d 58 41 56 49 48 56 40 4b b7 7b 58 2b 9c 48 7b 34 0f 9c d1 2b f5 7a 7b 58 2b 9c d5 93 7a b1 e7 88 7b 34 0f 9c da 70 c5 38 7e e5 87 27 b2 a8 8f 34 a1 4f 30 f2 30 d1 78 f5 3e 69 78 98 7a 99 7a 78 7e 78 18 b8 c1 34 0f 9c 34 1c 9c 3e 69 d5 3e 69 96 3e 69 b5 3f 69 88 67 b7 7b 72 d2 58 78 6e d5 d3 75 35 d4 75 f5 d3 75 58 78 6e 59 ; standard record; length=132, checksum=59 OK
data 00270 55 55 fc d5 d3 75 35 d4 75 f5 d3 75 34 70 18 d1 78 f5 e7 62 d5 e7 62 96 e7 62 b5 d8 62 88 67 b7 7b 72 d2 58 dd 61 d5 d3 75 35 d4 75 f5 d3 75 58 dd 61 d5 d3 75 35 d4 75 f5 d3 75 34 4f 18 d1 78 f5 3f 69 f5 d8 62 58 a3 d7 d1 78 f5 d8 1b d1 79 f5 bf 0c d1 38 51 86 f5 b0 0c d1 1c f5 b1 0c d1 78 f5 b4 0c f5 b2 0c f5 b3 0c f5 c8 0d f5 c9 0d f5 10 0f f5 11 0f f5 12 0f d1 78 f5 06 0f d1 78 f5 fe 01 ee ; standard record; length=132, checksum=ee OK
data 00269 55 55 fc f5 ff 01 f5 f0 01 f5 f1 01 f5 bb 02 f5 3c 04 f5 3d 04 f5 30 04 f5 c3 05 f5 c4 05 f5 c5 05 f5 19 06 f5 1a 06 f5 1b 06 f5 80 07 f5 81 07 f5 7c f8 f5 7d f8 f5 5f f9 f5 50 f9 f5 82 07 f5 40 fa f5 db fb f5 dc fb f5 dd fb f5 84 fc f5 85 fc f5 86 fc f5 52 fe f5 53 fe f5 54 fe f5 56 0a 10 d2 10 18 30 e0 30 f2 30 d5 cb 0d 88 7b b6 cb 0d d5 12 0f 88 7b b6 12 0f d5 d8 1b a8 7b 34 b1 1a b1 64 dd ; standard record; length=132, checksum=dd OK
data 00269 55 55 fc a8 75 d1 78 f5 d8 1b d5 d8 1b 88 83 34 bc 1a b1 b7 79 c7 a8 68 d5 ba 1b b1 78 88 7e 58 6d 1c 34 74 1c 34 bc 1a b1 f9 a8 70 d1 78 f5 d9 1b 34 bc 1a b1 fd a8 70 d1 79 f5 d9 1b 34 bc 1a b1 c1 a8 73 d1 87 35 e5 1b f5 e5 1b 34 bc 1a b1 e8 a8 73 d1 87 35 e6 1b f5 e6 1b 34 bc 1a b1 ed a8 6b d1 87 35 e5 1b f5 e5 1b d1 87 35 e6 1b f5 e6 1b 34 bc 1a b1 f0 a8 3a d1 78 f5 bd 1b d5 ba 1b b1 78 e1 ; standard record; length=132, checksum=e1 OK
data 00287 55 55 fc 88 7e 58 6d 1c 34 77 1c d5 eb d1 fd d0 d5 ec d1 fd b7 7b d1 f5 e1 d1 58 4d 72 58 3d 72 d5 d3 75 35 d4 75 f5 d3 75 58 3d 72 d5 d3 75 35 d4 75 f5 d3 75 58 45 72 34 bc 1a b1 d5 a8 2c d1 79 f5 bd 1b d5 ba 1b b1 78 a8 c2 58 4d 72 d5 5a d0 fd d0 d5 5b d0 fd d1 58 3d 72 d5 d3 75 35 d4 75 f5 d3 75 58 3d 72 d5 d1 df fd d0 d5 d2 df fd d1 d1 c9 f5 d7 df 58 3d 72 d5 d3 75 35 d4 75 f5 d3 75 58 90 ; standard record; length=132, checksum=90 OK
oczywiście wszystkie rekordy danych muszą mieć na końcu status OK! (CRC rekordu musi się zgadzać)
3) Należy dokonać konwersji tak wyedytowanego pliku HEX to postaci RAW, używając a8cas-convert:
a8cas-convert.exe -fr 01_Triad_normal_strona_A_cuted.hex 01_Triad_normal_strona_A_cuted.tcx
4) Teraz należy uruchomić tcx_rle_decoder z pakietu TCX Tools:
python3 tcx_rle_decoder.py 01_Triad_normal_strona_A_cuted.tcx
Input file is 01_Triad_normal_strona_A_cuted.tcx and the file size is 28416 bytes.
TCX data loaded, checking data...
>>> Data encrypted! Trying to use calculated XOR key 0x78 <<<
Header is: $ffff
block 000: $0600-$0649 ($004a)
block 001: $02e0-$02e1 ($0002)
block 002: $6000-$b9c0 ($59c1)
block 003: $37d9-$5000 ($1828)
block 004: $02e0-$02e1 ($0002)
block 005: $7878-$7878 ($0001)
^^^^^^^^^: Invalid block header detected! Processing stopped.
Input data processing done, 5 correct block(s) processed, generating output file...
Output file is 01_Triad_normal_strona_A_cuted.tcx.xex and the file size is 29261 bytes.
Processing done, output file written, IN/OUT file size diff is 845 byte(s).
Informacja o błędach na końcu pliku jest normą, większość plików nagranych z użyciem Turbo Copy 3/4 ma na końcu śmiecie, lub zbędne zera, co po wykonaniu operacji XOR daje zazwyczaj jakieś bezdurne bajty na końcu pliku. Pythonowy skrypt stara się to wykryć i wyeliminować, ale nie zawsze się to udaje, można próbować wtedy poprawić taki plik HEX ręcznie przed konwersją, ew. poprawić strukturę pliku .XEX po konwersji.
5) po konwersji można dokonać weryfikacji pliku wygenerowanego przez tcx_rle_decoder, używajac chkxex.py:
python3 chkxex.py 01_Triad_normal_strona_A_cuted.tcx.xex
Input file is 01_Triad_normal_strona_A_cuted.tcx.xex and the file size is 29261 bytes.
Header is: $ffff
block 001: $0600-$0649 ($004a)
block 002: $02e0-$02e1 ($0002) ---> RUN $0600
block 003: $6000-$b9c0 ($59c1)
block 004: $37d9-$5000 ($1828)
block 005: $02e0-$02e1 ($0002) ---> RUN $0600
File 01_Triad_normal_strona_A_cuted.tcx.xex is OK!
Tym razem udało się wszystko bez najmniejszego problemu, wygenerowany plik ma poprawną strukturę i uruchamia się bez najmniejszego problemu.
Jeżeli chce się zachować oryginalny loader i oryginalną strukturę pliku i wygenerować z tego plik CAS w takim formacie, trzeba by ręcznie poprawić pliki HEX powstałe po konwersji, w tym wypadku widzę że a8cas-convert nie potrafi dobrze zadekować 6-bajtowego rekordu zabezpieczającego i robi sobie z tego rekord FSK, pewnie trzeba by się tym pobawić. Jeżeli zależy Ci na pliku CAS z tego WAV-a mogę spróbować odtworzyć strukturę tego pliku taka by był poprawnie wczytywany przez loader po konwersji do CAS. W załączniku dodaje wszystkie pliki które powstały podczas eksperymentów które opisałem wyżej.