![]() |
Reverse Engineering der NLT II - Druckversion +- Crystals-DSA-Foren (https://www.crystals-dsa-foren.de) +-- Forum: Allgemeines zur Nordlandtrilogie DOS (https://www.crystals-dsa-foren.de/forumdisplay.php?fid=20) +--- Forum: Technische Werkstatt (https://www.crystals-dsa-foren.de/forumdisplay.php?fid=34) +--- Thema: Reverse Engineering der NLT II (/showthread.php?tid=5383) |
RE: Reverse Engineering der NLT II - Obi-Wahn - 19.07.2025 (19.07.2025, 17:12)NewProggie schrieb: Kannst du sagen, was genau schief läuft bei Windows? Ich habe mir bisher nur die Webseite des Projekts angeguckt, ich habe noch keinen Checkout/Download gemacht. Ich kenne mich mit dem Build über GitHub überhaupt nicht aus. Ich sehe diese Fehlermeldung: RE: Reverse Engineering der NLT II - NewProggie - 19.07.2025 (19.07.2025, 17:29)Obi-Wahn schrieb:(19.07.2025, 17:12)NewProggie schrieb: Kannst du sagen, was genau schief läuft bei Windows? Ah, so, nein, die Jobs sind chronologisch von unten nach oben sortiert. Oben ist stets der neueste und der ist grün: https://github.com/NewProggie/BrightEyes/actions/runs/16389311718 RE: Reverse Engineering der NLT II - Obi-Wahn - 27.07.2025 Kleine Zwischenmeldung. Henne bastelt fleißig weiter. Die Namen und die Beschreibungen der Code-Einträge werden auch immer verständlicher für mich als Laie. Globale Variablen werden bearbeitet und alle möglichen Spielbestandteile sind zu erkennen. ![]() RE: Reverse Engineering der NLT II - HenneNWH - 30.07.2025 Das möchte ich meinen. Der Zustand des Codes ist nur was für Hartgesottene. Bei Schick gibt es aktuell eine Binäräquivalenzcodedifferenz von 4740 Bytes. Diese Zahl sollte mit fortschreitenden Arbeiten kleiner werden. Heut morgen waren es 4879 Bytes. Eine andere Fortschrittsmetrik ist die Anzahl der auskommentierten Zeilen in der Datei symbols.h. Dort sind es aktuell 15%. Für Interessierte: Der Status des Spiels (Savegame) befindet sich als zusammenhängender Bereich im Datensegment. Dieser Bereich muss gewissen Vorgaben entsprechen, damit gespeicherte Spielstände auch wieder geladen werden können. Diese globalen Variablen beginnen mit gs_ anstelle von g_. In diesem Bereich befinden sich u.A. auch die Farbpaletten für die Dämmerzustände. ![]() Leider befinden sich auch ein paar Zeiger (Variablen welche Speicheradressen enthalten) in diesem Bereich. ![]() Das konnte bisher u.U zu seltsamen Effekten und kaputten Spielständen führen. Beispiele sind:
![]() RE: Reverse Engineering der NLT II - HenneNWH - 02.08.2025 Zwischenstand: * Binäräquivalenzcodedifferenz = 4520 Bytes * auskommentierte Zeilen in der symbols.h = 22 % * es kompilieren auch schon einige Dateien (53 / 112) mit dem GCC RE: Reverse Engineering der NLT II - Obi-Wahn - 03.08.2025 Danke für die Infos und Erklärungen! Ich bin immer wieder beeindruckt, dass du dich durch diese Unmengen an Programmcode durchkämpfst! RE: Reverse Engineering der NLT II - HenneNWH - 04.08.2025 Es kann nur besser werden. Das ist gerade wie mit der Machete durch den Dschungel fetzen. ![]() ![]() ![]() Im Moment werden Speicherzugriffe aufs Datensegment durch echte, lesbare globale Variablen und hoffentlich gut benannte Datenstrukturen ersetzt. Am Ende dieses Kapitels von Phase 2 sollten keine ds_read/write()-makros und auch kein p_datseg mehr im Code sein und die symbols.h wird dann nicht mehr gebraucht. Die Binäräquivalenzcodedifferenz ist ein wichtiger Wert, der mir herauszufinden hilft, ob einzelne Variablen ein Vorzeichen haben oder nicht. Wird dieser Wert kleiner oder bleibt gleich ist alles in Ordnung. Wird er größer stimmt etwas nicht und ich muss dann genauer nachgucken. Das bessert unsaubere Arbeiten meinerseits aus der Anfangszeit von Bright-Eyes aus, falls ich mal ds_readb() anstelle von ds_readbs() benutzt habe. Das entspricht dem Lesezugriff auf eine Variable im Datensegment vom Typ: unsigned char bzw. signed char. Manchmal spielt es keine Rolle, manchmal kann so etwas auch zu Fehlern/Abweichungen geführt haben. Wenn der Code dann in Reinform ist, ist es dann auf jeden Fall einfacher diesen Dingen nachzugehen. Zwischenstand: * Binäräquivalenzcodedifferenz = 4098 Bytes (geht gegen 0) * auskommentierte Zeilen in der symbols.h = 28.6 % (geht gegen 100%) * es kompilieren auch schon einige Dateien (54 / 112) mit dem GCC RE: Reverse Engineering der NLT II - Obi-Wahn - 05.08.2025 (04.08.2025, 18:41)HenneNWH schrieb: Es kann nur besser werden. Das ist gerade wie mit der Machete durch den Dschungel fetzen. Genau den Eindruck habe ich beim Blick auf die Commits. Durchhalten! ![]() ![]() RE: Reverse Engineering der NLT II - HenneNWH - 05.08.2025 Zwischenstand: * Binäräquivalenzcodedifferenz = 4064 Bytes * auskommentierte Zeilen in der symbols.h = 31.6 % * es kompilieren auch schon einige Dateien (57 / 112) mit dem GCC RE: Reverse Engineering der NLT II - HenneNWH - 06.08.2025 Zwischenstand: * Binäräquivalenzcodedifferenz = 4064 Bytes * auskommentierte Zeilen in der symbols.h = 36.1 % * Kompilierte "seg*.cpp" Dateien mit dem GCC = (63 / 110) RE: Reverse Engineering der NLT II - HenneNWH - 07.08.2025 Meine Vermutung, dass mit den Zeigern in den Spielständen etwas kaputt gehen kann hat sich bestätigt: ![]() Bei der ersten Begegnung wird ein Held ausgewählt und die Speicheradresse (RAM-DOS) im Spielstand abgelegt. Verändert man die Reihenfolge der Helden kann das zu Storyinkonsistenzen und/oder Abstürzen führen. Dazu kommt: unter DOS hat ein Zeiger eine Größe von 4 Byte, unter 64-bit Systemen 8 Byte. Ob mir da eine Lösung einfällt... RE: Reverse Engineering der NLT II - Obi-Wahn - 07.08.2025 Was genau sind Zeiger? Maus-Zeiger? RE: Reverse Engineering der NLT II - vonGratenfels - 07.08.2025 (07.08.2025, 19:05)HenneNWH schrieb: Bei der ersten Begegnung wird ein Held ausgewählt und die Speicheradresse (RAM-DOS) im Spielstand abgelegt. Heiland Sack! Zitat:Dazu kommt: unter DOS hat ein Zeiger eine Größe von 4 Byte, unter 64-bit Systemen 8 Byte. Doppelter Heiland Sack!! (07.08.2025, 20:34)Obi-Wahn schrieb: Was genau sind Zeiger? Maus-Zeiger?Andere sagen auch Senkel oder Sack. Im Zusammenhang mit "das geht mir auf den ..." ;-) Aber im Ernst: Anstatt den Wert einer Variable weiter zu geben, z.B hero="Alrik" gibt man die Speicheradresse, in der der Wert gespeichert ist, weiter und implizit auch, wieviel Speicherplatz ab dieser Adresse für diesen Wertreserviert ist. Eine Variable hat also 2 Komponenten. Explizit Komponente 1, den Inhalt und Komponente 2, die Speicheradresse, wo der Inhalt steht. Komponente 2 heisst im Deutschen Zeiger, im Original "pointer". Wenn jetzt im Spielstand "Alrik" stehen sollte und dort nur eine Speicheradresse steht, die evtl kürzer als der Platz für Alrik ist, kommt man in Teufelsküche, wenn an diesem Speicherplatz plötzlich kein Alrik sondern ein Borbarad ist. Oder sonst irgendetwas undefiniertes. Welcome to C. Fals ich Unsinn erzählt haben sollte, bitte berichtigen. Ist schon etwas länger her. RE: Reverse Engineering der NLT II - HenneNWH - 07.08.2025 Eine Zeiger/Pointer ist eine Variable welche die Speicheradresse einer Variable enthält. Der Compiler kennt zu Compilezeit auch deren Typ. Es ist zu erwarten, dass etwas Undefiniertes an dieser Speicheradresse steht (siehe Bild). Storytechnisch sollte der vom Einhorn erwählte Held/Heldin anders kenntlich gemacht werden. Die Speicheradresse kann sich ebenso ändern wie die Position in der Gruppe. Zwischenstand: * Binäräquivalenzcodedifferenz = 4042 Bytes * auskommentierte Zeilen in der symbols.h = 40.2 % * Kompilierte "seg*.cpp" Dateien mit dem GCC = (65 / 110) RE: Reverse Engineering der NLT II - cmfrydos - 08.08.2025 Verstehe ich das richtig: Unter DOS wurde der Spielstand nicht serialisiert, sondern 1:1 auf die HDD geschrieben, in der Annahme, dass beim nächsten Start wieder alles an derselben Adresse gespeichert wird? Wild, aber damals, ohne viel malloc/new und mit fast nur globalen Variablen, vermutlich eine valide Möglichkeit. Auf modernen Systemen gilt diese Annahme nicht, da Speicheradressen durch Mechanismen wie ASLR variieren und das Speicherlayout von Compiler, Linker und Betriebssystem beeinflusst wird. Ich bin nicht wirklich in der Sache drin und könnte es falsch verstanden haben, aber ich würde hier einfach einen (De-)Serialisierer schreiben. Also die Zeiger beim Speichern auflösen in so etwas wie „3. Held“ und beim Neueinlesen wieder entsprechend setzen. Hilft natürlich nicht, wenn der Held dann im laufenden Spiel entfernt wird. ich vermute, er wird dann ausgenullt. In diesem Fall würde, wie hier im Screenshot zu sehen, ein „\0“ gelesen, was aber ein Bug im Originalspiel wäre und separat behoben werden müsste. Edit: Wenn man Spielstände zwischen Versionen kompatibel halten will, kann man beim Speichern statt wie vorgeschlagen „3. Held“ den Wert DosHeldenStartOffset + 3 * Heldengröße ablegen. Beim Einlesen lässt sich der Index mit (value - DosHeldenStartOffset) / Heldengröße bestimmen und der Zeiger entsprechend setzen. RE: Reverse Engineering der NLT II - HenneNWH - 08.08.2025 Ganz genau, cmfrydos. Der Spielstatus (gs_) ist aktuell in der Datei datseg.cpp und "nur" ein zusammenhängender Bereich von globalen Variablen. Bsp.: Code: Bit8s gs_datseg_status_start = 0; // ds:0x2d34, 99 = game finished, area of the datseg that is stored one to one in savegame files Die sauberste Lösung ist, ein unbenutztes Bit in der Datenstruktur des Helden/Heldin zu definieren. Ist der Held nicht mehr in der Gruppe kommt das Einhorn bei zweiten mal gar nicht. Wenn das Spiel erfolgreich beendet wurde, wird dieses Bit gelöscht und der Held/Spielstand bleibt mit Sternschweif kompatibel. Der Zeiger (gs_unicorn_hero_ptr) und die Position (gs_unicorn_hero_pos) gehören überhaupt nicht in den Spielstand, weil diese Daten synchron gehalten werden müssten. Single-Point-of-Truth! Das kann aber erst umgesetzt werden, wenn Schick in dem Zustand ist, wie der Charaktergenerator zum jetzigen Zeitpunkt. Das heißt auch, dass diese Information aus "alten" Spielständen nicht mehr genau rekonstruiert werden kann. Hoffe dass das bei den anderen Zeigern nicht sosehr ins Gewicht fällt. RE: Reverse Engineering der NLT II - HenneNWH - 08.08.2025 Zwischenstand: * Binäräquivalenzcodedifferenz = 4036 Bytes * auskommentierte Zeilen in der symbols.h = 42.5 % * Kompilierte "seg*.cpp" Dateien mit dem GCC = (71 / 110) RE: Reverse Engineering der NLT II - Obi-Wahn - 09.08.2025 Wow, du legst echt ein Tempo vor! ![]() RE: Reverse Engineering der NLT II - HenneNWH - 09.08.2025 @Obi-Wahn: Will ja auch mal fertig werden. Zu den 12 Zeigern/Pointer im Spielstand: Es ist mir heute gelungen für zwei Exemplare davon eine Lösung zu finden. Ein Zeiger wird im Code nur in den Spielstand geschrieben und nie gelesen. Das tolle daran ist, dass dieser Codeteil nie ausgeführt wurde und somit alles in Ordnung ist. ![]() Exemplar Nummer 2 ist der Zeiger auf die Reisekarte. Dieser wird beim Lesen der Karte aus der SCHICK.DAT jedesmal neu gesetzt, hat aber während des Spiels immer denselben Wert. Für die Zukunft habe ich diesen Zeiger anderswo platziert und im Spielstand steht dort 0000. ![]() In der DOS-Version muss der Zeiger aus Binäräquivalenzcodedifferenzgründen erstmal im Spielstand bleiben, was durch das Aktualisieren jedoch keine negativen Auswirkungen hat, außer dass der Spielstand pseudo-randomisierten Datenmüll enthält. Zwischenstand: * Binäräquivalenzcodedifferenz = 3958 Bytes * auskommentierte Zeilen in der symbols.h = 46.7 % * Kompilierte "seg*.cpp" Dateien mit dem GCC = (71 / 110) RE: Reverse Engineering der NLT II - Lippens die Ente - 10.08.2025 ihr legt ja ein ganz schönes Tempo vor, wenn das für Sternenschweif und Riva genauso fix geht dann Hut ab, meinen Respekt von einem NLT-Fan der vom Programmieren aber keine Ahnung hat. |