Themabewertung:
  • 1 Bewertung(en) - 5 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Reverse Engineering der NLT II
Nach wie vor bin ich immer wieder mal an meinem BrightEyes-Fork zu Gange. Meine Hauptmotivation ist das Reparieren von Bugs (von denen es leider furchtbar viele gibt). Gerne hätte ich auch die ganzen Bugfixes aus dem Patch von NRS mit drin. Ich stelle mir vor, nach Lust und Laune Listen von bekannten Bugs durchzugehen und zu reparieren; dabei ergibt sich dann auch gerne, dass der umliegende Code verstanden werden muss, den ich dann gleich kommentieren kann und Variablen umbenennen (falls noch nicht geschehen).

Teilweise betreffen die Bugs ja auch die Daten in der SCHICK.DAT. Momentan werden Daten "on the fly" im Quellcode repariert, z.B. diese Reparatur eines Rechtschreibfehlers.

Denkbar wäre es aber auch, sich einen Patch-Mechanismus für die SCHICK.DAT zu überlegen und das ganze als Diff-Dateien in BrightEyes zu verwalten, siehe z.B. diesen Beitrag und die darauf folgenden. Ich möchte hier also eine Diskussion darüber anstoßen, wie das ganze am bsten in BrightEyes umgesetzt werden sollte und was es dabei zu beachten gilt, damit man es hoffentlich gleich von Anfang an "richtig" macht

Persönlich tendiere ich zur gepatchten SCHICK.DAT, weil es sich sauberer anfühlt. Aber der Mechanismus muss erstmal aufgesetzt werden. Er dürfte grob aus den folgenden Teilen bestehen dürfte:
  • Entpacken und Zerlegen der SCHICK.DAT
  • Anwenden der Patches auf die Einzelteile
  • anschließendes Packen und Zusammenbauen zu der gepatchten SCHICK.DAT

Über die folgenden Punkte muss man sich wohl auch Gedanken machen:
  • Das Patchen muss abhängig von Präprozessor-Flags gestaltet sein, so dass die angewendeten Patches passend zu den im Quellcode aktivierten Modifikationen vorgenommen werden.
  • Momentan wird ja auf Textblöcke der SCHICK.DAT durch Befehle der Art get_tx(<nummer>) zugegriffen. Wenn das Reparieren eines Bugs das Erstellen weiterer Textblöcke erforderlich macht, dann verschiebt sich evtl. die <nummer>. Wie geht man damit am besten um? Fügt man die neuen Textblöcke immer hinten an, damit sich die Nummern davor nicht verschieben? (Aber was macht man, wenn durch Präprozessor-Flags nur eine Auswahl der neuen Textblöcke aktiviert wurde?) Gibt es eine Möglichkeit, hier eine vernünftige Zwischenebene einzufügen? Letztlich sind die <nummer>n ja auch wieder nur "magic numbers", die idR besser durch Konstanten-Bezeichner ersetzt werden sollten.
Und last but not least die Bitte: In diesen technischen Fummeleien habe ich wenig Erfahrung, kann mir da jemand helfen?

@gaor: Nachdem Henne ja nicht mehr mitmacht, geht meine Hoffnung zuerst in deine Richtung. Du hast ja auch das schlaue Tool schick-data-gui geschrieben, das ja auch die SCHICK.DAT ausliest.
Zitieren
Ich verstehe noch nicht, wann du genau die SCHICK.DAT patchen bzw. was genau du tun willst. Willst du ein Tool programmieren, das die SCHICK.DAT modifiziert (und ein Backup des Originals anlegt)? Oder willst du, dass die SCHICK.DAT on-the-fly beim Aufruf von Bright-Eyes gepatcht wird? Willst du ein Tool, das einen Patch auf die SCHICK.DAT anwendet, oder eines, das dabei hilft, neue Patches für die SCHICK.DAT zu generieren? Willst du einfach einen interaktiven Editor für die SCHICK.DAT? Wie genau kann ich dir bei irgendetwas davon helfen?

Vielleicht könntest du erstmal damit anfangen, in irgendeiner Art von Dokument alle Bestandteile der SCHICK.DAT zu sammeln, von denen dir bereits bekannt ist, dass sie Bugs verursachen und für einen Bug-Fix verändert werden müssten. Darauf aufbauend kann man besser beurteilen, welche Herangehensweise am effizientesten ist.

Das Tool schick-data-gui war mein Versuch, einen Reader für die SCHICK.DAT zu erstellen, um besser zu verstehen, was darin enthalten ist und in welchem Format. Aber es gibt noch unzählige Teile der SCHICK.DAT, die das Tool nicht "versteht". Wie genau würde dir das bei deinem Problem helfen?
Zitieren
Sehr schnelle Rückmeldung, wow!
(Gestern, 12:07)gaor schrieb: Ich verstehe noch nicht, wann du genau die SCHICK.DAT patchen bzw. was genau du tun willst. Willst du ein Tool programmieren, das die SCHICK.DAT modifiziert (und ein Backup des Originals anlegt)?

Ja, in dieser Richtung. Mir schwebt folgendes vor:

Die originale SCHICK.DAT muss sich jeder selber an einer vordefinierten Position im BrightEyes-Verzeichnisbaum ablegen (klar, wegen Copyright) und dann einmalig ein Skript starten, das daraus die Einzelteile (wie z.B. TEXT.LTX) generiert und wieder an einer gewissen Position ablegt. Also von der Logik her recht ähnlich zum einmaligen Erstellen der Binär-Segmente, wofür die schick.exe abgelegt wird und dann per Skript der disassembler drübergeht.

Daneben gibt es dann noch Bearbeitungskopien aller in SCHICK.DAT enthaltenen Dateien, z.B. von TEXT.LTX, die natürlich nur lokal existieren (Copyright). In git werden nur Diff-Dateien verwaltet. Bei einem 'git pull' werden diese Bearbeitungskopien automatisch aus der lokalen Originaldatei + diff-Datei aus git erstellt. Bei 'git commit' werden die Diff-Dateien automatisch aus Bearbeitungskopie vs. Originaldatei erstellt und dann diese commitet.

Bei jedem Aufruf von 'make' sollte dann neben dem binary auch die dazu passende SCHICK.DAT erzeugt werden, d.h. die Bearbeitungskopien der Teile werden zu einer SCHICK.DAT zusammengebaut.

Jetzt ist noch die Frage, wo die Abhängigkeit von Präprozessor-Flags eingebracht werden kann. Das hatte ich mir zuvor zu einfach vorgestellt, glaube ich.
Hierzu fällt mir noch folgendes ein, was aber durchaus mit einigem Aufwand verbunden wäre: Man bräuchte -- zumindest für bestimmte Dateiformate wie LTX -- noch eine weitere Ebene, nämlich ein besser vom Menschen les- und bearbeitbares Dateiformat, z.B. was XML-artiges. Eine Grundform dieser Datei (auf die dann die diff-Dateien aufsetzen) -- nennen wir sie TEXT.LTX.xml -- muss sich per Skript aus TEXT.TLX erzeugen lassen und auch umgekehrt muss TEXT.LTX sich wieder per Skript aus TEXT.LTX.xml produzieren lassen. TEXT.LTX.xml bildet jetzt die lokale Bearbeitungskopie; die von git verwalteten Diff's setzen auf dieser Datei auf. Darin kann man jetzt bequem alles mögliche mit unterbringen, z.B. Kommentare, Abhängigkeiten von gewissen (Präprozessor-)Flags, oder zu jeder Textstelle auch einen zugehörigen Konstanten-Bezeichner, auf den der Quellcode dann Bezug nimmt (im Stil von get_tx(TX_ITEM_NAME_SWORD) anstelle von get_tx(23)). Die zugehörigen Definitionen "enum { ... TX_ITEM_NAME_SWORD = 23 ...};" müssten dann vor dem Compilieren automatisch aus der Datei TEXT.LTX.xml erzeugt werden.

Aber das ist nur das, was ich mir so zurechtgelegt habe. Vielleicht ist es aber auch unnötig kompliziert. Denkst du, dass das Vorgehen vernünftig ist, hast du andere Vorschläge?

(Gestern, 12:07)gaor schrieb: Vielleicht könntest du erstmal damit anfangen, in irgendeiner Art von Dokument alle Bestandteile der SCHICK.DAT zu sammeln, von denen dir bereits bekannt ist, dass sie Bugs verursachen und für einen Bug-Fix verändert werden müssten. Darauf aufbauend kann man besser beurteilen, welche Herangehensweise am effizientesten ist.

Mir geht es erstmal um die Text-Inhalte (Rechtschreibfehler, etc.), und darum, dass manche ID's von Gasthäusern im Original durcheinandergeraten sind (es gibt Gasthäuser in unterschiedlichen Orten mit derselben ID). Oder z.B. darum, dass es möglich ist den fahrenden Händler Kolberg in einem Haus in Clanegh anzusiedeln, so wie es im Patch von NRS auch passiert. Oder um den entgleisten Pixel bei den Hunden.

Vielleicht wäre es ein Anfang, sich erstmal auf ein sehr gut verstandenes Dateiformat wie .LTX zu konzentrieren.

Bei der technischen Umsetzung fühle ich mich unsicher. Für das Zerlegen und Zusammenbauen der Einzelteile gibt es wohl von euch schon passenden Code (nltpack). Dann die Frage, womit führt man das Patchen am besten durch, ist das standard diff-patch Paar dafür geeignet? Das ganze muss dann in das Makefile eingearbeitet werden und die ganzen für die Automatisierung nötigen Skripte erstellt werden usw.
Zitieren
(Gestern, 12:07)gaor schrieb: Das Tool schick-data-gui war mein Versuch, einen Reader für die SCHICK.DAT zu erstellen, um besser zu verstehen, was darin enthalten ist und in welchem Format. Aber es gibt noch unzählige Teile der SCHICK.DAT, die das Tool nicht "versteht". Wie genau würde dir das bei deinem Problem helfen?

Du weißt auf alle Fälle schon mal, wie man die SCHICK.DAT in die Einzelteile zerlegt. Und zumindest bei den gut verstandenen Formaten: Du hast es geschafft, den Inhalt auf den Bildschirm zu bringen! Da ist der Weg nicht weit, den Inhalt auch in ein (noch zu definierendes) XML-Format rauszuschreiben. Aber bitte verstehe mich nicht falsch: Das setzt natürlich voraus, dass du meinen Ansatz für vernünftig hältst, und dass du Lust hast, dich da einzubringen.
Zitieren
Ich möchte dich gerne unterstützen, weil ich's auch toll finde, dass du Zeit in Bright-Eyes investierst. Ich kann das angesprochene Thema aber noch nicht ganz überblicken. Für kleine Rechtschreibfehler wie den von dir verlinkten (https://github.com/siebenstreich/Bright-...0.cpp#L767) wäre ein Editor oder gar ein eigenes externes Format (XML) doch ein Overkill, oder nicht? Das ist doch verhältnismäßig elegant direkt im Code lösbar. Es geht ja nur um einzelne Bytes, da wäre es sogar denkbar, einfach direkt die Bytes in der SCHICK.DAT mit einem Hex-Editor zu manipulieren und mit xxd ein diff der Hexadezimal-Darstellungen zu erstellen, damit es auch andere einfach bei sich anwenden können. Genauso einfach wäre das Problem der Gasthäuser-IDs.

Welche Änderungen an der SCHICK.DAT für die übrigen von dir angesprochenen Probleme (Kolberg und entgleiste Pixel) nötig wären, kann ich nicht nachvollziehen, weil ich die Debatten nicht verfolgt habe. Deswegen meinte ich ja, es wäre gut, wenn es eine Übersicht gäbe, welche Arten von Änderungen benötigt werden, damit man besser beurteilen kann, welche Lösung sich anbietet.
Zitieren
(Gestern, 15:43)siebenstreich schrieb: Sehr schnelle Rückmeldung, wow!
[quote="gaor" pid='166743' dateline='1618657646']
Ja, in dieser Richtung. Mir schwebt folgendes vor:

Die originale SCHICK.DAT muss sich jeder selber an einer vordefinierten Position im BrightEyes-Verzeichnisbaum ablegen (klar, wegen Copyright) und dann einmalig ein Skript starten, das daraus die Einzelteile (wie z.B. TEXT.LTX) generiert und wieder an einer gewissen Position ablegt.

Hendriks nltpack kann schick.dat aus- und auch wieder verpacken. Mir ist noch nicht klar, worin der Vorteil des von Dir beschriebenen Verfahrens besteht.
Walisischer Käsetoast [Bild: kaesetoast.png]
Zitieren
@gaor: Ich überblicke auch noch nicht, wie komplex die an der SCHICK.DAT nötigen Änderungen werden. Aber ich denke, es sollte einheitlich sein. Kleinere Sachen per Binärpatch zu korrigieren und größere dann anders, das gefällt mir nicht so recht.

@gaor, Rabenaas:
Prinzipiell sehe ich die folgenden 3 Möglichkeiten, die ich mal versuche mit Vor- und Nachteilen aufzuzählen. Bitte schreibt mal eure Meinung dazu!

1. SCHICK.DAT bleibt gleich und man macht "Hot-Fixes" direkt im Code. Das passiert schon (sehr vereinzelt) in BrightEyes. Schnell und dirty, aber vielleicht sollte man es einfach so machen. Da bin ich irgendwie hin- und hergerissen. Unten ein Beispiel dazu. Eine potentieller Makel ergäbe sich, wenn es viele Code-Stellen gibt die auf denselben fehlerhaften Eintrag in der SCHICK.DAT zugreifen, denn dann muss man den Hot-Fix an jeder solchen Stelle vornehmen. Potentiell könnte das bei Rechtschreibfehlern in Gegenstandsnamen auftreten (es gibt da Basilikenzunge -> Basili*s*kenzunge, Wolfmesser -> Wolf*s*messer), hab ich mir noch nicht genauer angeschaut.

2. Man repariert die SCHICK.DAT per Hex-Editor oder sonstwie und verbreitet Binärpatches. Das gefällt mir nicht so gut, weil es intransparent ist und nicht recht zu dem offenen Ansatz von BrightEyes passt. Außerdem gibt es die Gefahr, dass man sich bei größeren oder vielen Änderungen verheddert.

3. Man bricht (zumindest die textlastigen) Daten der SCHICK.DAT auf ein besser lesbares Format herunter und macht dort die Änderungen. Dann kann man diffs von Textdateien machen und hat außerdem die Möglichkeit, Kommentare vorzunehmen und Änderungen fallweise zu aktivieren oder deaktivieren. Die Vorstellung gefällt mir schon gut. Der Nachteil ist, dass dafür einige Vorarbeit nötig ist und man in Gefahr läuft, sich damit zu verzetteln.


Hier noch ein konkretes Beispiel für einen Hot-Fix:
An zwei Stellen (Stelle 1, Stelle 2) wird in BrightEyes ein Textfenster angelegt, das es zuvor nicht gab.
Anders als bei den anderen Textfenstern kommt der Textinhalt dann also nicht aus der SCHICK.DAT, sondern direkt aus dem binary.
Die Frage ist jetzt, ob man das so gut findet, oder eher nicht. Es ist mindestens uneinheitlich. Aber man kann natürlich argumentieren, dass der Zweck die Mittel heiligt und das Problem damit kurz und bündig gelöst ist.
Zitieren
(Vor 10 Stunden)siebenstreich schrieb: Ein potentieller Makel ergäbe sich, wenn es viele Code-Stellen gibt, die auf denselben fehlerhaften Eintrag in der SCHICK.DAT zugreifen, denn dann muss man den Hot-Fix an jeder solchen Stelle vornehmen.
Dann definiert man halt eine Funktion, die man an jeder solchen Stelle aufruft. Das halte ich jetzt für keinen besonders großen Aufwand.


(Vor 10 Stunden)siebenstreich schrieb: Man [..] verbreitet Binärpatches.
Da stimme ich zu. Ich finde es auch sehr unbefriedigend, wenn man nur die Binär-Files rausgibt und nirgendwo nachvollziehbar und reproduzierbar dokumentiert ist, was und wie geändert wurde.


(Vor 10 Stunden)siebenstreich schrieb: Man bricht (zumindest die textlastigen) Daten der SCHICK.DAT auf ein besser lesbares Format herunter und macht dort die Änderungen.
Wenn es nur um Text geht, braucht man kein lesbareres Format, weil der Text ja schon so lesbar ist. Du kannst dir ja mal die SCHICK.DAT mit einem Hex-Editor anschauen. Der Text ist bis auf ein paar Sonderzeichen direkt lesbar. Solche Patches könnte man mit diff und xxd erstellen. Gemessen an allen Beispielen, die du uns hier bisher vorgestellt hast, finde ich es aber immer noch am einfachsten, die Änderungen direkt im Quellcode vorzunehmen - so wie es bereits getan wird.


(Vor 10 Stunden)siebenstreich schrieb: Die Frage ist jetzt, ob man das so gut findet, oder eher nicht.
Ich finde es gut so und sähe nur zwei Gründe, warum man es anders machen sollte: 1. Falls es wirklich sehr viele sehr große Änderungen an der SCHICK.DAT gibt, die sehr viele unübersichtliche zusätzliche Codezeilen benötigen. 2. Man will generell nicht nur Bugs fixen, sondern komplexere Mod-artige Änderungen vornehmen (sowas schwebte mir ursprünglich mal vor, als ich das schick-data-gui Tool begonnen habe, bin davon aber wieder abgekommen).

Am Rande bemerkt verstehe ich nicht, warum Henne an den von dir verlinkten Stellen so umständliche Char-Arrays anstatt String-Literale benutzt:
Code:
const unsigned char add_line[110] = { 0x40, 0x3c,'I','C','H',' ',
    'G','L','A','U','B','E',',',' ',
    'I','C','H',' ',
    'M','U','S','S',' ',
    'E','S',' ' ,'N','U','R',' ',
    'N','O','C','H',' ','E','I','N',' ',
    'E','I','N','Z','I','G','E','S',' ',
    'M','A','L',' ','V','E','R','S','U','C','H','E','N','!', 0x3e,' ',
    'M','U','R','M','E','L','T',' ',
    '%', 's',' ',
    'A','L','S',' ',
    '%', 's',' ',
    'W','I','E','D','E','R',' ',
    'A','U','F',' ',
    '%','s','E',' ',
    'F', 0x9a, 'S','S','E',' ',
    'K','O','M','M','T','.',
    '\0'
    };

Ich habe ja nicht so viel Ahnung von C und C++, aber ginge das nicht auch so?
Code:
const unsigned char add_line[110] = "\x40\x3cICH GLAUBE, ICH MUSS ES NUR NOCH "
                                    "EIN EINZIGES MAL VERSUCHEN!\x3e MURMELT %s "
                                    "ALS %s WIEDER AUF %sE F\x9aSSE KOMMT.";
Und muss man dafür überhaupt eine neue Variable definieren, oder kann man das Literal nicht direkt ins Funktionsargument reinschreiben?
Zitieren
Die Schreibweise mit mehrzeiligen Textkonstanten habe ich noch nie (bewusst) gesehen. Hab's gerade mit einem modernen gcc probiert. Damit passt alles.
Walisischer Käsetoast [Bild: kaesetoast.png]
Zitieren
(Vor 9 Stunden)gaor schrieb:
(Vor 10 Stunden)siebenstreich schrieb: Ein potentieller Makel ergäbe sich, wenn es viele Code-Stellen gibt, die auf denselben fehlerhaften Eintrag in der SCHICK.DAT zugreifen, denn dann muss man den Hot-Fix an jeder solchen Stelle vornehmen.
Dann definiert man halt eine Funktion, die man an jeder solchen Stelle aufruft. Das halte ich jetzt für keinen besonders großen Aufwand.

Hatte mir fast gedacht dass das kommt. :)
Ich will eigentlich mit dem Code möglichst nahe am Original bleiben, nachdem es sich ja nicht um was Eigenständiges handelt, sondern um einen Bugfix von bestehendem Code. Und da hab ich irgendwie eine Blockade, wegen einem fehlenden Buchstaben eine neue Funktion einzubauen. Aber letztlich ist das natürlich allemal besser als an 10 Stellen im Code synchron dieselbe Ergänzung einzufügen.
Und trotzdem werde ich das dumpfe Gefühl nicht ganz los: Konsequenterweise sollte das Übel an der Wurzel gepackt werden, und die liegt nunmal in der SCHICK.DAT und nicht im Code, der die Dateien weiter verarbeitet.

(Vor 9 Stunden)gaor schrieb: Wenn es nur um Text geht, braucht man kein lesbareres Format, weil der Text ja schon so lesbar ist. Du kannst dir ja mal die SCHICK.DAT mit einem Hex-Editor anschauen. Der Text ist bis auf ein paar Sonderzeichen direkt lesbar.

Stimmt natürlich. Was mich halt etwas stört ist, dass ich da nichts kommentieren kann und keine Präprozessor-Maschinerie zur Verfügung habe.

(Vor 9 Stunden)gaor schrieb: Solche Patches könnte man mit diff und xxd erstellen. Gemessen an allen Beispielen, die du uns hier bisher vorgestellt hast, finde ich es aber immer noch am einfachsten, die Änderungen direkt im Quellcode vorzunehmen - so wie es bereits getan wird.
[...]
Ich finde es gut so und sähe nur zwei Gründe, warum man es anders machen sollte: 1. Falls es wirklich sehr viele sehr große Änderungen an der SCHICK.DAT gibt, die sehr viele unübersichtliche zusätzliche Codezeilen benötigen.
ok, das ist eine klare Aussage, vielen Dank! Daran werde ich mich erstmal halten und schauen, wie weit ich komme.

(Vor 9 Stunden)gaor schrieb: 2. Man will generell nicht nur Bugs fixen, sondern komplexere Mod-artige Änderungen vornehmen (sowas schwebte mir ursprünglich mal vor, als ich das schick-data-gui Tool begonnen habe, bin davon aber wieder abgekommen).

Nein, darum geht es mir nicht. Ich will lediglich "digitale Denkmalpflege" betreiben, um die sehr treffenden Worte von Henne (oder von dir?) zu benutzen.

Für vernünftiges Modding hielte ich es für zielführender, wenn man das Spiel neu und v.a. sauber programmiert und sich dabei die Logik aus BrightEyes abschaut. Vorher muss man sich sehr gründlich überlegen, wie man die Schnitstelle zu den Daten-Beschreibungsdateien möglichst flexibel gestalten will. Dieses Gefühl hatte ich von Anfang an und seitdem ich durch BrightEyes etwas in die Untiefen des Quellcodes geblickt habe, hat sich das nur deutlich verhärtet.

(Vor 9 Stunden)gaor schrieb: Am Rande bemerkt verstehe ich nicht, warum Henne an den von dir verlinkten Stellen so umständliche Char-Arrays anstatt String-Literale benutzt [...]

Das hatte ich mich auch gefragt. Hat es vielleicht was mit BCC-Kompatibilität zu tun?
Zitieren




Benutzer, die gerade dieses Thema anschauen: 2 Gast/Gäste