Jakoś na GG się zgadać nie możemy, będzie więc chyba lepiej jak przedstawię wyjaśnienie tutaj.Nie wiem czy nie podpada to pod jakiś punkt regulaminu, ale najlepiej się rozumie podobno na przykładach, więc spróbujmy zatem zmienić text który jest wyświetlany po zginięciu drużyny.
Potrzebne będą:
-FCEUX(Wersja z debugerem)
-Jakiś hex edytor.
Nasze cele:
-Wykryć jak są zapisywane pointery w grze.
-Zmienić długość textu.
Włączamy grę w FCEU i doprowadzamy naszego bohatera do upadku(giniemy w walce).
By było szybciej radzę wykonać save state tuż przed wyświetleniem się textu do którego pointera szukamy.
Czekamy aż pierwsza literka z textu się wyświetli i pauzujemy emulator.
Z menu emulatora wybieramy
Debug-->Name Table Viewer.
To okienko pokazuje nam Name Table(Ułożenie tilesów na ekranie).Najeżdzamy myszką na pierwszą literkę textu(w naszym przypadku będzie to "T").W dolnym prawym rogu Name Table Viewer mamy różne dane odnośnie wskazywanego przez myszkę punktu w Name Table Viewer.Interesuje nas
PPU Address-to do tego adresu gra musi zapisać by grafika była wyświetlona, oraz
Tile ID-Jest to numer naszego kawałka grafiki.
Punkt Kontrolny:
PPU address dla naszego textu to: 2686
Tile ID to: 1D
Zamykamy okienko Name Table Viewer i włączamy debugger
Debug-->Debugger.
Musimy zrobić Break point.
W górnym prawym rogu klikamy "Add...", wpisujemy PPU Address nowe okienko, zaznaczamy "Write", Zaznaczamy "PPU Mem" i w "Condition" wpisujemy "A == #1D" a następnie klikamy OK.
O, właśnie tak.
Teraz ładujemy Savestate.I wywołujemy tekst, znaczy moment w którym ma być wypisana literka "T" na ekranie.Grę powinien przerwać debuger, wskazując na kod który zapisuje literkę.
Kod: Zaznacz cały
:6030:8D 07 20 STA $2007 = #$00 //Zapisz literkę do Name Table
:6033:A9 FE LDA #$FE //Wczytaj kolejną literkę
:6035:8D 07 20 STA $2007 = #$00 //Zapisz literkę do Name Table
:6038:A9 FE LDA #$FE //Wczytaj kolejną literkę
:603A:8D 07 20 STA $2007 = #$00 //Zapisz literkę do Name Table
:603D:A9 FE LDA #$FE //Wczytaj kolejną literkę
:603F:8D 07 20 STA $2007 = #$00 //Zapisz literkę do Name Table
Od razu widać że coś jest nie tak.Ale to dla Ciebie bez znaczenia.Klikając strzałkę w górę(2 razy) znajdziesz polecenie
Czyli to tu jest trzymana literka "T"...Ale skąd się tam wzięła? Kod jest na $6000 czyli WRAMie(dodatkowym RAMie dla NESa)
Adres 602E-jego trzeba przepytać.Sam 602E to "A9", więc trzeba o 1 większy adres.Znowu robimy break pointa.W address wpisujemy 602F, zaznaczamy write, i zaznaczamy CPU mem. Tym razem okienko powinno wyglądać tak:
Klikamy OK, usuwamy poprzedni breakpoint i ładujemy save'a.Debugger znów nam przerwie.Pamiętamy by kliknąć dwa razy(czasem trzeba 3) na strzałkę w górę w lewej części debuggera zanim zaczniemy cokolwiek analizować.
Kod: Zaznacz cały
0F:E26D:B1 00 LDA ($00),Y @ $6706 = #$1D //Odczytaj z adresu zapodanego na $6706
0F:E26F:9D 11 60 STA $6011,X @ $602F = #$00 //Zapisz to do adresu $602F
No i czemu tak? Przypomina mi moją własną grę na NESa w bardzo wczeeesnym stadium.Ale to nie ważne.Dobra, wiemy że pointer na $00 każe wczytywać z $6706.Czyli ze znaleźliśmy pointer? NIE, niestety, ale nie.Pointery tekstów(te które nas interesują) nigdy(w teori można, w praktyce...to bez sensu) nie bedą nas kierować do RAMu.To oznacza że ten pointer był obliczany, a my musimy ustalić skąd nasza literka "T" była odczytana i zapisana do $6706.Kolejny break point...Address $6706, Write CPU, A ==#1D.Wczytujemy save state i znów czekamy na debugger.Aż strach myśleć co tym razem wymyśli.Tym razem należy kliknąć więcej razy strzałkę do góry.Niestety, nie ma na to formuły, trzeba znać assemblera i kropka.
Kod: Zaznacz cały
0F:D328:BD 2F 02 LDA $022F,X @ $022F = #$1D //Odczytaj literkę z buffera
0F:D32B:C9 FF CMP #$FF //Sprawdź czy to nie przypadkiem FF
0F:D32D:F0 0B BEQ $D33A //Jeśli tak to skocz do adresu $D33A
0F:D32F:91 00 STA ($00),Y @ $6706 = #$FE //Zapisz Literkę do buffera na $6706
No, w końcu coś bardziej normalnego...(Nawiasem mówiąc gry zwykle używają jednego buffera, czyli w innej grze tutaj by doprowadził Cię pierwszy breakpoint).Czyli przed zapisaniem do buffera $6706 nasze "T" jest zapisane do buffera na $22F? Wiesz co teraz zrobić...Address 22F,CPU,Write, A == #1D, wczytanie save state.
Kod: Zaznacz cały
0F:D576:B1 00 LDA ($00),Y @ $94AB = #$1D //odczytaj naszą nieszczęsną literkę
0F:D578:C9 30 CMP #$30 //Sprawdź czy to nie 30
0F:D57A:D0 06 BNE $D582 //Jeśli nie to skocz do $D582
0F:D57C:20 D2 D5 JSR $D5D2 //Nasza literka to nie 30, czyli bez znaczenia
0F:D57F:4C 85 D5 JMP $D585 //Nasza literka to nie 30, czyli bez znaczenia
0F:D582:9D 2F 02 STA $022F,X @ $022F = #$1C //Zapisz literkę do buffera.
Hm, teraz nasza literka jest wczytywana do buffera z Adresu większego niż $8000.To oznacza że w końcu została wczytana z ROMu a nie z kolejnego buffera.Pointer który na to wskazuje znajduje się na $00.
Włączmy
Debug-->Hex editorI spójrzmy co znajduje się na adresie $00 i $01.
Powinno być AB 94.Tworzymy nowy break point: address 00, Write, CPU mem, A == #AB. AB, ponieważ jest LO adressu i jest małe prawdopodobieństwo ze trafi się drugi taki sam.Wczytujemy save state'a i czekamy na debugger.Uwaga:By nie było pomyłki-Krzyk debuggera interesuje nas tylko wtedy gdy ma być wypisana literka na ekranie, nie wcześniej(W naszym przykładzie gra zapisuje też AB do 00 kiedy ramka jest w połowie "rozwijania się"-to nas nie interesuje).
Kod: Zaznacz cały
0F:D568:AD 44 02 LDA $0244 = #$AB //Wczytaj LO dla pointera
0F:D56B:85 00 STA $0000 = #$3A //Zapisz go do LO pointera właściwego
0F:D56D:AD 45 02 LDA $0245 = #$94 //Wczytaj HI pointera
0F:D570:85 01 STA $0001 = #$68 //Zapisz go do HI pointera właściwego
Mimo że rozumiem dla czego tak musi być(NES nie może używać pointerów jeśli są zapisane na wyższych adresach niż $FE), nie rozumiem jednak czemu nie mogli od razu zapisywać pointerów do $00, podejrzewam ze był w tym czasie zajęty.Dobra, miejmy już to za sobą.Break point na 244,Write,CPU mem i A == #AB da nam:
Kod: Zaznacz cały
0F:F8BE:8E 45 02 STX $0245 = #$94 /Zapisz HI pointera do buffera
0F:F8C1:8D 44 02 STA $0244 = #$AB //Zapisz LO pointera do buffera
0F:F8C4:AD 44 02 LDA $0244 = #$50 //Odczytaj LO pointera
0F:F8C7:48 PHA //Zabezpiecz go w stosie
0F:F8C8:AD 45 02 LDA $0245 = #$94 //To samo z HI pointera
0F:F8CB:48 PHA
0F:F8CC:20 2A D8 JSR $D82A //Przygotuj dialog(?)
0F:F8CF:68 PLA //Odzyskaj "zachomikowane" HI
0F:F8D0:8D 45 02 STA $0245 = #$94 //Zapisz
0F:F8D3:68 PLA
0F:F8D4:8D 44 02 STA $0244 = #$50 /To samo z LO
0F:F8D7:20 52 D2 JSR $D252 //Zacznij wypisywać literki na ekranie.
0F:F8DA:60 RTS //zakończ funkcję
Wbrew pozorom już kończymy.Pointery Są w tej funkcji zapisywane na podstawie tego co znajduje się w rejestrach X i A.Więc naszym najlepszym wyjściem będzie wyjście "przed funkcję".By tego dokonać wpisujemy w małe pole(okno debugger) o nazwie "PC" pierwsze dwa bajty wypisane poniżej("Stack").Wpisujemy je w
odwrotnej kolejności.W naszym przypadku wpiszemy w PC "94A1".Klikamy na guzik "Seek PC".Przeskoczymy w miejsce przed wywołaniem funkcje zapisującej do buffera pointerów.Przesuwamy nieco w górę i....
Kod: Zaznacz cały
02:949B:A2 94 LDX #$94 //HI textu(pointera)
02:949D:A9 AB LDA #$AB //LO textu(pointera)
02:949F:20 BE F8 JSR $F8BE /Zapisz HI i LO do Buffera, i wywołaj dialog.
Tak, to koniec, alleluja!
Hmm..zła wiadomość: Nie ma tu tablic pointerów(jak w większości gier).Tu zawsze trzeba będzie tak kombinować by znaleźć miejsce pointerów.I żeby nie było tak różowo- dialogi które mają długość większą niż jeden ekran okienka które je wyświetla musi być wywołane osobno.
O tym jak Zwiększyć długość textu, zmienić pointery i wykorzystać wolne miejsce napiszę jak się wyśpię.Późno już, testu nie zaliczyłem, i wogóle markotny dziś jestem.Jak coś to pisz.
Punkt Kontrolny:
-Cel "Wykryć jak są zapisywane pointery w grze" zrealizowany.