Themabewertung:
  • 5 Bewertung(en) - 5 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Reverse Engineering der NLT
Hi Zurgrimm!

Zurgrimm schrieb:Die Angaben in eckigen Klammern sind eher als Orientierung für mich gedacht. Dort steht, woher der Aufruf der Probenfunktion ursprünglich kam. "JMP" heißt einfach, dass die Probefunktion über einen "JMP"-Befehl aufgerufen wurde. Dadurch kann man später weitere Funktionen des Spiels identifizieren.

Ich habe dein Tool bisher noch ned getestet oder im Source begutachtet, da du aber offenbar Asm-Instruktionen auffängst, mal 'ne Frage, werden auch CALL's abgefangen, und die verschiedenen Varianten (NEAR, FAR, usw.)?

Ich frage, weil es mehrere Möglichkeiten hier gibt, viele Compiler optimieren z.B. ein:
CALL func
RET

zu:
JMP func

Wenn Du also nur JMP abfängst, aber ned CALL, kann es sein, dass Dir vieles durch den Lappen geht.
'func' ist in unserem Fall wohl die Randomize-Funktion
Desweiteren sind auch Aufrufe in folgenden Formen möglich:
JZ func
JNZ func
JC func
JNC func
etc.

Dies kann vom Compiler ebenso erzeugt werden.
JNZ .skip
CALL func
.skip: ...

wird zu:
JZ func
...

optimiert.

Achte auch drauf, dass indirekte Adressierungen erkannt werden, z.B. so was wie:
LEA EAX,func
CALL [EAX] bzw. JMP [EAX]

Als Tipp so generell, anstatt die ganzen Instruktionen einzeln zu tracen und gucken ob die auf 'func' zeigen, würde ich einfach checken, ob PC = func ist (also program counter), damit erwischste definitiv jedenfall dann alles.

Mit 'nem guten Debugger kannst da auch nach dem RET gucken dann von wo das aufgerufen wird (Stackframe) und so z.B. erkennen ob der Aufruf von einer Talentprobe kommt. So kannst auch dem Kampfsystem auf der Spur kommen, warum da noch nix angezeigt wird. Ich nehme an, dass da eher mit CALL gearbeitet wird. Warum das so wichtig ist, will ich mal an folgendem Asm-Code erläutern:
MOV AL, [ESI+0xC] // MU vom Char in ESI
MOV BL, 6 // 1W6
CALL func // Würfeln (was dein Logger ausgibt)
MOV DX,AX // Ergebnis vom Wurf sichern
MOV AL, [ESI+0XD] // KL vom Char im ESI
MOV BL, 6 // 1W6
CALL func // Würfeln
ADD DX,AX // Aktuellen Wurf vom vorherigen addieren
MOV AL, [ESI+0XF] // KK vom Char im ESI
MOV BL, 6 // 1W6
JMP func // Würfeln und Rücksprung zu anderer Funktion

Wenn Du jetzt nur JMP abfängst, dann übersiehste quasi die beiden CALLs und damit die Würfe, d.h. dein Logger würde nur die KK-Probe anzeigen aber ned die MU und KL-Probe, da diese per CALL aufgerufen werden.

Kann da übrigens, wenn's die Zeit erlaubt auch gerne ein bissel bei helfen.
Best regards,
:-) Basty/CDGS (-:
(18.04.2015, 18:12)BastyCDGS schrieb: Hi Zurgrimm!

Zurgrimm schrieb:Die Angaben in eckigen Klammern sind eher als Orientierung für mich gedacht. Dort steht, woher der Aufruf der Probenfunktion ursprünglich kam. "JMP" heißt einfach, dass die Probefunktion über einen "JMP"-Befehl aufgerufen wurde. Dadurch kann man später weitere Funktionen des Spiels identifizieren.

Ich habe dein Tool bisher noch ned getestet oder im Source begutachtet, da du aber offenbar Asm-Instruktionen auffängst, mal 'ne Frage, werden auch CALL's abgefangen, und die verschiedenen Varianten (NEAR, FAR, usw.)?
Dein Zitat stammt nicht von mir. Wenn Du mal in den Beitrag #295 (vom 05.08.2008) schaust, aus dem es stammt, dann wirst Du erkennen, daß Hendrik diesen Beitrag geschrieben hat. Es handelt sich um seinen Logger. Ich habe ihn nur genutzt und hatte einige Anmerkungen dazu geschrieben, auf die er dort geantwortet hat.
"Haut die Säbel auffe Schnäbel."
Hallo BastyCDGS,

schön dass sich hier mal jemand mit compilerspezifischen Codegenerierungskenntnissen im Forum auftaucht.
Herzlich wilkommen!

Der Logger ist nicht sehr allgemein, sondern sehr DOSBox- und SCHICK-spezifisch.

JMP-Instruktionen werden vom Logger mittlerweile nicht mehr abgefangen,
da es für SCHICK und SCHWEIF nicht notwendig ist (Erfahrungswerte).
Als Kompiler kam für SCHICK der Borland C++ 3.1 zum Einsatz (mit ausgeschalteter Optimierung).

Für die CALL-Instruktionen wird zwischen der NEAR- und der FAR-Variante unterschieden.
DOSBox benutzt intern eine abstraktere Funktion um CALL-Instruktionen zu emulieren (RealMode vs. ProtectedMode, usw.),
welche wir erweitert haben und benutzen. Dieselbe Funktion ist in DOSBox auch für die Ausführung von indirekt adressierte Calls zuständig,
und stellen somit kein Problem dar.
Sorry für die Verwechslung, Zurgrimm...aber nach drei Tavernenbesuchen die ganze Nacht und 'ner mißlungenden Probe auf Zechen in Thorwal kann das durchaus mal passieren... *gg*
Aber sieh's positiv, dank des erhöhten MU-Wertes hab ich immerhin die Probe, hier zu posten, bestanden... :-)

@HenneNWH
Das es ein C/C++-Compiler war, habe ich mir ohnehin schon gedacht, da ich damals die Schicksalsklinge auf dem Amiga mal disassembliert hatte und dabei nach gelungener Magiekunde-Probe üblichen C-Startup-Code fand. Borland war ja zu DOS-Zeiten quasi der Standardcompiler, habe ihn auch in der DOSBox noch am laufen, wenn ich den freilich ned mehr nutze, DJGPP ist da irgendwie interessanter ;)
Was mich allerdings überrascht ist, dass der Release-Build ohne Optimierungen stattfand. Das ist eigentlich sehr unüblich, macht aber in unserem Fall die Sache sehr leicht, da man auch ge-inlined-Funktionen so gut ausschließen kann, das war nämlich auch noch meine Überlegung, ob einige Calls ned erkannt werden, weil die Random-Fkt. direkt geinlined wurde vom Compiler.
Am Besten wäre, wenn jmd. von Attic noch die Debug-Symbole bekommt oder gleich den ganzen Source ;-)
Mir sind schon so einige Ideen gekommen, was man im Game noch verbessern könnte.
Z.B. wenn die Helden an einer Weggabelung auf der Karte ankommen das man hier die Helden aufteilen kann. Das geht ja bekanntlich bisher nur auf der 3D-Map.

Kurz Offtopic in dem Thread: Hat eigentlich mal jmd. das Game mit Perma-Death durchgezockt? Sprich, einmal irgendwo komplett den Heldentod sterben bedeutet auch komplett verkackt und von vorne anfangen?
Best regards,
:-) Basty/CDGS (-:
Die Sourcen von Schick sind verloren, aber, wie Du vielleicht schon gesehen hast, hat Hendrik mit BrightEyes schon eine ganze Menge deassemblieren können.

Afair hat Hendrik gerade eine Permadeathrunde laufen.
(18.04.2015, 21:20)Rabenaas schrieb: Die Sourcen von Schick sind verloren, aber, wie Du vielleicht schon gesehen hast, hat Hendrik mit BrightEyes schon eine ganze Menge deassemblieren können.

Afair hat Hendrik gerade eine Permadeathrunde laufen.

Evtl. ist auch die Möglichkeit, dass die eigentliche Engine da a la ScummVM implementiert wurde, sprich Game-Interpreter, den man ggf. auch auf die Datenfiles laufen lassen kann.
Hab mich da selber halt noch ned reingehängt bei Schick.

EDIT: Ich komme auf ScummVM, weil die aktuellen ScummVM-Versionen selbst "Eye of the Beholder" abspielen können, also eine RPG-Engine, die man erstmal ned mit Monkey Island, Indiana Jones, Loom, etc. verbinden würde...auch wenn das 'ne andere Baustelle ist, würde mich nicht wundern, wenn DSA auch so 'nen Interpreter hätte, den man vllt. nutzen könnte um die Game-Engine exakt nachzubauen.
Best regards,
:-) Basty/CDGS (-:
Wenn Du in diesem Thread zurückliest, wirst Du feststellen, dass es hier umgekehrt läuft. Die Sourcen werden mit IDA disassembliert, dann von Hand aufbereitet, anschließend mit Borland wieder kompiliert und die Binaries verglichen. Henne hat bereits 794 von 1236 Funktionen umgesetzt. Die Ergebnisse landen hier:

https://github.com/Henne/Bright-Eyes

Die Interpretergeschichte und hoffentlich Mods kommen dann, wenn Brighteyes nichts mehr emulieren muss und auf den DosBox-Unterbau verzichten kann.
(18.04.2015, 21:01)BastyCDGS schrieb: Kurz Offtopic in dem Thread: Hat eigentlich mal jmd. das Game mit Perma-Death durchgezockt? Sprich, einmal irgendwo komplett den Heldentod sterben bedeutet auch komplett verkackt und von vorne anfangen?

Du findest meinen Permadeath-Durchlauf für Schick im Thread Sechs Tempelzehntverweigerer in Thorwal, mir sind im Verlaufe des Spiels immerhin 3 der sechs Helden gestorben. Die Gruppe ist mittlerweile in Schweif importiert und leicht erweitert, nun sind es Vier Tempelzehntverweigerer im Svellttal. Inspiriert zu dem Durchlauf hatte mich HelmhamsterHDs Nehmen, wie es kommt.

(18.04.2015, 21:46)BastyCDGS schrieb:
(18.04.2015, 21:20)Rabenaas schrieb: Die Sourcen von Schick sind verloren, aber, wie Du vielleicht schon gesehen hast, hat Hendrik mit BrightEyes schon eine ganze Menge deassemblieren können.

Evtl. ist auch die Möglichkeit, dass die eigentliche Engine da a la ScummVM implementiert wurde, sprich Game-Interpreter, den man ggf. auch auf die Datenfiles laufen lassen kann.
Hab mich da selber halt noch ned reingehängt bei Schick.

Wobei man zum Disassemblieren sagen muss, dass ich da zwar einiges gemacht habe, in der Zwischenzeit aber mein Namensvetter HenneNWH einen deutlich größeren Anteil disassembliert hat. Ich habe mich eher auf das Bauen von Konvertern und Viewern verlegt. Sicher sind wir uns allerdings darin, dass Schick (und ebenso Schweif) nicht als Engine/Game-Interpreter gebaut wurde. Im Gegenteil, selbst die Spieldaten stecken teilweise fest in der .EXE, z.B. Waffentabellen. Skript-Dateien, wie man sie von neueren Spielen kennt, sucht man in den Archiven vergebens.
Hallo, ich bin's - der Bart von Fidel Castro. Und mir ist total langweilich nie geschnitten wurde.
I'm a roleplayer. My dice are like my relationships: platonic and unlucky.
(19.04.2015, 10:35)Hendrik schrieb: Sicher sind wir uns allerdings darin, dass Schick (und ebenso Schweif) nicht als Engine/Game-Interpreter gebaut wurde. Im Gegenteil, selbst die Spieldaten stecken teilweise fest in der .EXE, z.B. Waffentabellen.
Das ist auch noch bei Riva so. Die Codebase der NLT ist eine extrem "gewachsene" die mindestens bis auf Attics Spirit of Adventure zurück geht (wenn sie nicht sogar noch älter ist, möglicherweise Lords of Doom).
Das ist vermutlich ein Trugschluss. Guido Henkel hat hier gesagt, dass Schick komplett neu geschrieben wurde.
(19.04.2015, 07:57)Rabenaas schrieb: Wenn Du in diesem Thread zurückliest, wirst Du feststellen, dass es hier umgekehrt läuft. Die Sourcen werden mit IDA disassembliert, dann von Hand aufbereitet, anschließend mit Borland wieder kompiliert und die Binaries verglichen. Henne hat bereits 794 von 1236 Funktionen umgesetzt. Die Ergebnisse landen hier:

https://github.com/Henne/Bright-Eyes

Die Interpretergeschichte und hoffentlich Mods kommen dann, wenn Brighteyes nichts mehr emulieren muss und auf den DosBox-Unterbau verzichten kann.

So, ich habe grad mal das Repo ausgecheckt und gebaut. Ging problemlos mit make -j16. Wo finde ich den Asm-Code vom IDA? Den konnte ich auf die schnelle nicht finden. Ich arbeite hier unter Linux, und IDA ist Win32, daher die Frage...
Best regards,
:-) Basty/CDGS (-:
(19.04.2015, 10:35)Hendrik schrieb:
(18.04.2015, 21:01)BastyCDGS schrieb: Kurz Offtopic in dem Thread: Hat eigentlich mal jmd. das Game mit Perma-Death durchgezockt? Sprich, einmal irgendwo komplett den Heldentod sterben bedeutet auch komplett verkackt und von vorne anfangen?

Du findest meinen Permadeath-Durchlauf für Schick im Thread Sechs Tempelzehntverweigerer in Thorwal, mir sind im Verlaufe des Spiels immerhin 3 der sechs Helden gestorben. Die Gruppe ist mittlerweile in Schweif importiert und leicht erweitert, nun sind es Vier Tempelzehntverweigerer im Svellttal. Inspiriert zu dem Durchlauf hatte mich HelmhamsterHDs Nehmen, wie es kommt.

Cool, das werde ich mir auf jedenfall mal reinziehen. Danke!

Zitat:
(18.04.2015, 21:46)BastyCDGS schrieb:
(18.04.2015, 21:20)Rabenaas schrieb: Die Sourcen von Schick sind verloren, aber, wie Du vielleicht schon gesehen hast, hat Hendrik mit BrightEyes schon eine ganze Menge deassemblieren können.

Evtl. ist auch die Möglichkeit, dass die eigentliche Engine da a la ScummVM implementiert wurde, sprich Game-Interpreter, den man ggf. auch auf die Datenfiles laufen lassen kann.
Hab mich da selber halt noch ned reingehängt bei Schick.

Wobei man zum Disassemblieren sagen muss, dass ich da zwar einiges gemacht habe, in der Zwischenzeit aber mein Namensvetter HenneNWH einen deutlich größeren Anteil disassembliert hat. Ich habe mich eher auf das Bauen von Konvertern und Viewern verlegt. Sicher sind wir uns allerdings darin, dass Schick (und ebenso Schweif) nicht als Engine/Game-Interpreter gebaut wurde. Im Gegenteil, selbst die Spieldaten stecken teilweise fest in der .EXE, z.B. Waffentabellen. Skript-Dateien, wie man sie von neueren Spielen kennt, sucht man in den Archiven vergebens.

Schade, was mir nämlich als Idee auch vorschwebte, ist 'ne Art FRUA zu machen für DSA auf Basis des disassemblierten Codes. Wer FRUA nicht kennt, es ist ein Abenteuergenerator für die AD&D Gold Box Engine, mit denen man komplett
eigene Abenteuer erstellen kann und die dann wie Pools of Darkness zocken kann.
Best regards,
:-) Basty/CDGS (-:
Schau mal hier https://github.com/Henne/BrightEyes-reveng

Was sollte denn daran hindern, einen Dungeongenerator zu schreiben?
(19.04.2015, 14:52)Rabenaas schrieb: Schau mal hier https://github.com/Henne/BrightEyes-reveng

Was sollte denn daran hindern, einen Dungeongenerator zu schreiben?

Der Dungeongenerator selber ist da weniger das Problem. Ich dachte da eher an das Eventsystem für die Dialoge und ähnliches. So das man halt auch komplexe Abenteuer machen kann. Im Idealfall sollte man natürlich auch die Originalabenteuer exakt nachbauen können.

Ich habe mir eben mal seg031.cpp angeschaut, eat_while_drinking. Da fiel mir was auf:
hero += 0x6da in der for-Schleife, das sieht mir ganz danach aus, als ob das sizeof (struct hero) ist, und könnte dementspr. angepasst werden.

EDIT: Die .CHR-Dateien sind 0x6da = 1754 Bytes groß. Passt also. Kann es sein (habe die Save Game Funktion noch nicht gefunden, dass das Hero Feld einfach direkt geschrieben wird ohne Änderungen beim save_game? Wenn dem so
sein sollte, können wir direkt die struct von 'nem Savegame-Editor mit Source übernehmen und haben schon mal
das meiste. Dann wird auch bei vielen Funktionen klarer, was sie bewirken.
Ich bin gerade bei seg036_00ae. Da werden 1 BP oder 2 BP abgezogen und die Richtung eines Chars gewechselt. 1 BP kostet ja entweder laufen oder Gegenstand wegwerfen. 2 BP Gegenstand / Waffe wechseln...

EDIT2: Für die hero-struct hätte ich noch 'ne Idee, und zwar anhand der Erkenntnisse aus dem Savegame-Editoren sollte
es ein leichtes sein, die entspr. Felder zu zuordnen. Das macht auch den Code deutlich besser lesbar wenn da steht
hero->health bzw. hero.health statt hero+0x21 oder so ;)
Ein kleinen Fehler habe ich grad noch in der symbols.h gefunden, es muss KNIFE heißen ned KNIVE ;)

EDIT3: So ich denke die ENEMIES hab ich auch, es ist:
p_datseg + 0xd0df

sizeof (struct enemy) = 0x3e

Das sieht man in der KI_can_attack_neighbour sehr gut, wo die Abfrage kommt ob target zw. 10 und 30 (es gibt 20 Gegner max, richtig?).
Best regards,
:-) Basty/CDGS (-:
Da gibt es irgendwo ein Skript, dass einige Hexzahlen mit Labels überschreibt. Lass das erst mal laufen.
Ich sehe, das Gegner offenbar ab Offset 10 anfangen...allerdings hat man ja nur 7 Charaktere max. Wozu sind 8 und 9 gedacht, für beschwörte Wesen? Kann man mehr als zwei Wesen in einem Kampf beschwören?
Vorschlag für diese checks statt target >= 10 && target < 30 und so kram würde ich das in die symbols.h als define aufnehmen. MAX_CHARS = 10 MAX_ENEMIES = 20, so kann man das nachher mal anpassen und z.B. mehr als 20 Gegner im Kampf erlauben uns so:
target >= MAX_CHARS && target < (MAX_CHARS + MAX_ENEMIES)

EDIT:
(19.04.2015, 16:54)Rabenaas schrieb: Da gibt es irgendwo ein Skript, dass einige Hexzahlen mit Labels überschreibt. Lass das erst mal laufen.

Du meinst tools/add2sym.sh? Das hatte ich bereits.

EDIT2:
Ich hätte übrigens noch 'ne Idee. Mal nochmal die Amiga-Schick zu disassemblieren und auch auf dieser Basis portieren. Der Vorteil ist, dass bei der m68k-Version der ganze Segmentscheiß vom Realmode wegfällt.

Zur struct-Implementation noch was:
add2sym.sh macht ja nur erstmal die Basispointer soweit und nicht die structs selber. Ich schlage ein weiteres File vor structs.h oder so und dort landet dann Kram wie typedef struct Hero { ... } HERO;
Ein Shellskript dazu add2structs.sh ersetzt dann bspw. hero + 0x2E entspr. durch hero->health.
Ich schlage vor nach dem Namen des typedefs lowercase zu suchen, um die Variable zu finden.

EDIT3:
Für die Heroes könnten wir das hier als Vorlage erstmal nehmen, um die struct aufzubauen:
https://github.com/NewProggie/DSA1-Saveg...c/hero.cpp

EDIT4: Initiale structs.h angehängt.

EDIT5: structs.h verfeinert, u.a. als Kommentar Hex-Offset zur Base addiert, damit Skripte die entspr. Position automatisch in den *.cpp Files ersetzen können.


Angehängte Dateien
.zip   structs.h.zip (Größe: 1,06 KB / Downloads: 4)
Best regards,
:-) Basty/CDGS (-:
Wahnsinnsarbeit, Basty, sehr "schick" ;-)

Ich hoffe, mit deinen Erkenntnissen kommen wir hier nochmal ein paar Schritte weiter!
Hallo Basty,

(19.04.2015, 15:37)BastyCDGS schrieb: EDIT2: Für die hero-struct hätte ich noch 'ne Idee, und zwar anhand der Erkenntnisse aus dem Savegame-Editoren sollte
es ein leichtes sein, die entspr. Felder zu zuordnen. Das macht auch den Code deutlich besser lesbar wenn da steht
hero->health bzw. hero.health statt hero+0x21 oder so ;)

das hast Du sehr gut erkannt.
Solche Änderungen werden aber erst in der zweiten Phase des RE gemacht.
Im Moment bin ich noch froh, dass Bright-Eyes auch auf BigEndian Maschinen läuft.
Wenn diese Umstellung schon jetzt erfolgen würde, würde dieses Feature verloren gehen.
Bei den hart-codierten Daten wird das auch kein Problem, aber die Datenfiles sind alle im LittleEndian Format. :D


Deine header-files nehm ich gern ins repo mit auf,
aber sie werden erst benutzt wenn der Code komplett nachgebaut ist.

Die Amiga-Version von SCHICK zu disassemblieren halte ich nicht für sehr sinnvoll,
da sicherlich dieselben C-Sourcen benutzt wurden.
Abgesehen von Hardwarezugriffen und Betriebssystem-besonderheiten wird es kaum
Unterschiede geben.

Interessanter wäre imho, wenn Du den für die Amigaversion verwendeten Kompiler ausfindig machen könntest,
und mal versuchst einzelne Dateien von Bright-Eyes damit zu übersetzten.
Dann kannst Du schauen ob diese Codesequenzen im Amiga-Binary auftauchen.
BTW, wie groß ist denn die Programmdatei in der Amigaversion?
Vor Pfingsten wird noch mal released:

Ersetzte Funktionen (Segmente sind komplett identisch)
  • seg026: Textloader, Savegames
  • seg050: Steigern
  • seg061: Tempel (noch nicht aktiv), Helden (aufnehmen, entlassen, löschen), Wunder
  • seg106: Inventar

Sonstiges:
  • seg025: erbosten Bürger reaktiviert
  • seg053: Heiler reaktiviert
  • seg047: Automap aktiviert

Was kommt als Nächstes?
  • seg062: Um Wunder bitten.
  • seg061: Tempel aktivieren
  • Dialogcode
  • Händler
  • Stadtcamp
  • Check: Skripte zum Vergleichen der Binärdateien (erfordert eigenen Borland C++ 3.1 Compiler)
  • mehr Dungeon-Handler nachbauen

Statistik:
  • Es sind 812 von 1236 Funktionen sind nachgebaut (65,70%).
  • Davon sind 763 identisch mit dem Originalcode.
  • Nach Byte-Metrik sind schon 79,44% fertig

Bemerkung: In diesem Release wurden Funktionen ersetzt, welche mit den Spielständen und den Charakterdateien arbeiten. Bitte sichert vorher eure Spielstände und CHR-Files, da ich keine Garantie dafür übernehme.
Ich habe einige Tests unter x64-Linux,Win32 und PowerPC-Linux durchgeführt und es hat funktioniert.

Viele Grüße,
HenneNWH
Donnerwetter, seid ihr schon weit. Taugt das Tool schon als Probenanzeiger in Schweif/Riva?
Hacke Tau, Kumpels!

Ihr seid Freunde der alten NLT? Freunde des Mikromanagements? Ihr sucht eine neue Herausforderung, weil euch die NLT zu leicht war?

Dann spielt doch mal Schicksalsklinge HD 1.36 von Crafty Studios!




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