Themabewertung:
  • 5 Bewertung(en) - 5 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Reverse Engineering der NLT
(13.02.2017, 11:33)llm schrieb: Der Fehler kommt wenn dein Segment > 64k ist

Das dachte ich auch und wunderte mich, warum diese Grenze denn schon überschritten sein soll. Inzwischen hab ich gerafft, dass ja schon für die Variable ds fast das ganze Segment belegt wird. Wenn ich die Deklaration von ds rausnehme, passt natürlich wieder alles. :wall:
(14.02.2017, 00:11)gaor schrieb:
(13.02.2017, 11:33)llm schrieb: Der Fehler kommt wenn dein Segment > 64k ist

Das dachte ich auch und wunderte mich, warum diese Grenze denn schon überschritten sein soll. Inzwischen hab ich gerafft, dass ja schon für die Variable ds fast das ganze Segment belegt wird. Wenn ich die Deklaration von ds rausnehme, passt natürlich wieder alles. :wall:

wie machst du jetzt den Umbau im Detail?
Ich hatte ja oben meine Variablendeklarationen verlinkt: http://pastebin.com/sSgNjd13 Die Deklarationen hat mir ein Python-Script aus den Einträgen in der symbols.h erstellt. Da sind aber noch ca. 270 Deklarationen, die als Datentyp ein struct oder einen Pointer haben. Die müssen jetzt per Hand durchgeschaut werden (die meisten structs werden glücklicherweise wenige Probleme bereiten). Danach werde ich mir überlegen, wie ich überprüfe, dass das mit dem Originaldatensegment übereinstimmt. Hauptproblem ist, zu verstehen, welche Teile des Datensegments von BCC automatisch erstellt werden und woher die Lücken kommen, die in der symbols.h nicht beschrieben sind (die Zeilen mit "// ? ...").
Zitat:Die Deklarationen hat mir ein Python-Script aus den Einträgen in der symbols.h erstellt. Da sind aber noch ca. 270 Deklarationen, die als Datentyp ein struct oder einen Pointer haben.

das ist ja schon mal sehr gut

Zitat:Danach werde ich mir überlegen, wie ich überprüfe, dass das mit dem Originaldatensegment übereinstimmt.

wie ich schon geschrieben habe würde sich da einen umgebender struct nutzen - dann kannst du darauf ein sizeof mit Prüfung machen und das Macro offsetof(http://www.cplusplus.com/reference/cstddef/offsetof/) für die Prüfung der lokalen Anordnung in dem struct
und #pragma pack damit dir nicht BCC,VS,gcc-Alignment-Unterschiede Probleme machen - und da du eh schon ein Python-Script hast ist das ja eine paar Zeilen Spielerei

ob du jetzt nachher g_ds.xyz oder g_xyz schreibst ist auch egal weil ihr eh schnell nach vollständiger Portierung beginnen werde aufzuräumen und strukturieren

auch die Inhaltsüberprüfung - wenn du auch komplett auf eigenen Initialisierungscode gehst - kannst du dann leicht mit
einem einzigen memcmp machen

ich würde aus deinem Script automatisch auch Füllbytes oder Unbekannt-Felder aus Bytes generieren - dann kannst du dich auf das Bekannte konzentrieren und machst keine Fehler
Es gibt jetzt eine vollständige Deklaration des Original-Datensegments: https://gist.github.com/tuxor1337/08cdaf...c95d466c18

Diese Deklarationen enthalten aber noch unzählige implizite struct-Datentypen. Außerdem sind Pointer entweder als void-Pointer oder als long-Werte deklariert. Aber von den Byte-Werten her müssten diese Deklarationen letztlich das Original-Datensegment rekonstruieren.

Als nächstes wäre dann wohl das fällig, was llm vorschlägt: Ein struct erzeugen, dass all die globalen Variablen als Felder enthält und die gleiche Größe hat wie das aktuelle Array "ds". Dann macht man einen Check, dass das Datensegment jetzt wirklich mit dem Original übereinstimmt und schließlich castet man die Variable "ds" auf dieses struct und arbeitet damit.

Jemand Lust, das mal zu machen? :D
Zitat:Jemand Lust, das mal zu machen?

dein Python-Script macht doch schon fast alles

dann musst du ja nur noch die Byte-Addresse jeweils mitführen und kannst damit dann den Startoffset mit Füllbytes exakt auf deine ds:0x.... Angabe legen, die offsetof-Asserts generieren und falls die Daten homogen genug sind sogar daraus die Initalisierungsroutine generieren - würde ich so mache - weniger Fehler und man kann wegen der Prüfungen dann trotzdem noch Hand anlegen

kennst du auch die Größe der jeweiligen Variable? oder lässt sich diese aus der Variablendeklaration ermitteln?

Poste doch mal dein Script
Seid ihr sicher, dass ihr damit keine neue Abhängigkeit vom BCC einbaut? Wie kommt man denn vom gecasteten struct zu "ganz normalen" vom Compiler allozierten Variablen auf dem Heap?
(15.02.2017, 08:54)Rabenaas schrieb: Seid ihr sicher, dass ihr damit keine neue Abhängigkeit vom BCC einbaut? Wie kommt man denn vom gecasteten struct zu "ganz normalen" vom Compiler allozierten Variablen auf dem Heap?

ich habe keine Ahnung was du meinst - kannst du die Frage umformulieren?

Code:
...keine neue Abhängigkeit vom BCC einbaut?

wie soll man deswegen eine BCC abhaengigkeit reinbekommen?
das gleiche macht jeder anderen C Kompiler genauso - oder meinst du das offsetof Macro und #pragma - die kann man unter #defines verstecken (weil ja auch nur zur Validierung gebraucht) damit es auf allen Platformen kompiliert - nach der vollständigen Portierung ist das alles sowieso egal und der Code kann komplett umgebaut werden - es geht nur darum so lange wie nötig exakt gleich zu sein - bis alles nur noch auf Symbole statt per Offsets verweist

Zitat:Wie kommt man denn vom gecasteten struct zu "ganz normalen" vom Compiler allozierten Variablen auf dem Heap?

wenn du die ds-Variable auf den struct castest kannst du "ds" weiter als Bytewolke - oder eben über den struct strukturiert zugreifen - und "vom Compiler allozierten Variablen auf dem Heap" gibt es technisch gar nicht - oder was meinst du? und selbst wenn es die geben würde ist es doch egal wo sie liegen - Hauptsache der Zugriff ist identisch
Vielen Dank gaor für das schnelle erstellen der CPP-Datei.
Ich habe sie eben bei mir getestet und soweit funktioniert es prima, nur dass das Datesegment jetzt etwas zu klein ist, wenn ich DS_SIZE auf 1 setzte.

Ich habe allerdings noch ein paar kleine Änderungen, welch ich jetzt committen werde.
Es wäre schön, wenn du dann einen Pull-Request mit den Daten machen kannst.
Eine wichtige Sache noch, die Deklaration von ds muss vor der ersten Variable passieren, da sonst anderer Code erzeugt wird.

Weiteres vorgehen:
Mit etwas Trickserei konnte ich ein Binary erzeugen, in welchem der ganze Code an der richtigen Stelle steht.
Wenn gaors Daten gemerged sind, wird evtl. noch ein Tool benötigt mit dem die Datensegmente aus der SCHICKM.EXE und der neuen
BLADEM.EXE (in temp) verglichen werden können. Zu Not geht auch "dd" und "cmp".
(15.02.2017, 09:33)llm schrieb: ich habe keine Ahnung was du meinst - kannst du die Frage umformulieren?
Die Frage war echt nicht genial formuliert. :pfeif:

Ich meinte eigentlich gar nicht den BCC selbst, sondern ein identisches Kompilat (durch den BCC) auf Byteebene, für das hier einige Umwege in Kauf genommen werden. Oder meint ihr, die globalen Variablen wurden schon im Original in ein struct gepackt, auf das dann ein Speicherbeich gecastet wurde? Ich meine, das wäre ein ziemlich seltsames Programmierparadigma. Vermutlich wurde das etwas konventioneller gehandhabt.

(15.02.2017, 09:33)llm schrieb: und selbst wenn es die geben würde ist es doch egal wo sie liegen - Hauptsache der Zugriff ist identisch
Da bin ich mir eben nicht ganz sicher. Hängt davon ab, ob nur das Kompilat "schön" sein soll, oder auch der Code. Aber ok, das Verfahren mit dem struct ist ein Fortschritt. Mit "vom Compiler allozierten Variablen auf dem Heap" meinte ich globale Variablen a la "struct nlt_timer_t licht_an_timer;" o.ä.
(15.02.2017, 11:10)HenneNWH schrieb: Ich habe allerdings noch ein paar kleine Änderungen, welch ich jetzt committen werde.
Es wäre schön, wenn du dann einen Pull-Request mit den Daten machen kannst.
Eine wichtige Sache noch, die Deklaration von ds muss vor der ersten Variable passieren, da sonst anderer Code erzeugt wird.
Okay, ds also vor der ersten Variable deklarieren.

Bevor ich für irgendwas einen PR erstelle: Haben denn die Deklarationen der ganzen globalen Variablen Auswirkungen auf das mit gcc kompilierte Bright-Eyes? Macht das zum jetzigen Zeitpunkt irgendeinen Unterschied, wenn die da alle noch in der datseg.cpp herumfliegen?

Wenn ich aktuell eine DATSEG.OBJ baue und die dort enthaltenen Daten (mittels xxd) mit dem Datensegment der originalen SCHICKM.EXE vergleiche, kommt heraus, dass in der DATSEG.OBJ irgendwelche zusätzlichen Daten rumfliegen, deren Herkunft ich mir nicht erklären kann - allerdings erst ab Offset 0x0400 (gerechnet ab dem Beginn des Datensegments), davor stimmen die Datensegmente überein. Das bedeutet, dass mitten im Array "g_wearable_items_warrior" irgendwelche Daten eingefügt werden, die in der Deklaration gar nicht vorkommen?!

Übrigens gibt es eine neue Version meiner datseg.cpp: https://gist.github.com/tuxor1337/08cdaf...c95d466c18 Ich initialisiere jetzt auch Variablen, die den Wert 0 haben (hatte ich vorher uninitialisiert gelassen, dann sortiert er die aber in der DATSEG.OBJ weg).
Du benutzt doch auch den BCC, stimmts?
Hast Du im Verzeichnis rewrite_m302de/temp/ die Dateien BLADEM.EXE und BLADEM.MAP?
Das Datensegment solltest du am besten aus der BLADEM.EXE extrahieren.

(15.02.2017, 22:20)gaor schrieb: Bevor ich für irgendwas einen PR erstelle: Haben denn die Deklarationen der ganzen globalen Variablen Auswirkungen auf das mit gcc kompilierte Bright-Eyes? Macht das zum jetzigen Zeitpunkt irgendeinen Unterschied, wenn die da alle noch in der datseg.cpp herumfliegen?

Das macht zum jetzigen Zeitpunkt für die GCC-Version tatsächlich keinen nennenswerten Unterschied.
Es wird erst interessant, wenn die ds_*-Makros/Funktionen wirklich durch Zugriffe auf die Variablen ersetzt werden.
Dann wird aber das Skript bc_ready.sh nicht mehr funktionieren,
da die Adressen in den Objektdateien relativ zum Anfang der DATA, bzw. BSS Sektion der Objektdatei angegeben werden.

Ich halte es für die einfachste Lösung, wenn als nächster Schritt das Datensegment in der DOS-Variante von Bright-Eyes
mit dem der SCHICKM.EXE in Übereinstimmung gebracht wird.
Dann reicht es aus nur noch die Binärdateien zu vergleichen.

(15.02.2017, 22:20)gaor schrieb: Wenn ich aktuell eine DATSEG.OBJ baue und die dort enthaltenen Daten (mittels xxd) mit dem Datensegment der originalen SCHICKM.EXE vergleiche, kommt heraus, dass in der DATSEG.OBJ irgendwelche zusätzlichen Daten rumfliegen, deren Herkunft ich mir nicht erklären kann - allerdings erst ab Offset 0x0400 (gerechnet ab dem Beginn des Datensegments), davor stimmen die Datensegmente überein. Das bedeutet, dass mitten im Array "g_wearable_items_warrior" irgendwelche Daten eingefügt werden, die in der Deklaration gar nicht vorkommen?!

Das funktioniert so nicht. :no:

Der Grund dafür ist, dass die Daten- und Codebereiche in den OBJ-Dateien aufgeteilt werden.
Deshalb siehst du Verwaltungsdaten und Programmdaten gemischt.
Genau deswegen gibt es das tool dump_obj, welches die zusammengesetzten Codeteile aus den OBJ-Dateien extrahiert.

Eine datseg.obj gibt es jetzt nicht mehr, da die SCHICKM.EXE auch ein Segment (seg14) enthält,
dessen Größe von der Anzahl der gelinkten Objektdateien abhängt.
Die Datei datseg.cpp habe ich in seg002.cpp per include eingebunden.
(Kein guter Stil aber die Größe von seg14 ändert sich nicht.)


(15.02.2017, 22:20)gaor schrieb: Übrigens gibt es eine neue Version meiner datseg.cpp: https://gist.github.com/tuxor1337/08cdaf...c95d466c18 Ich initialisiere jetzt auch Variablen, die den Wert 0 haben (hatte ich vorher uninitialisiert gelassen, dann sortiert er die aber in der DATSEG.OBJ weg).

Das mag als guter Stil erscheinen, aber initialisierte Daten (DATA) und uninitialisierte Daten (BSS) werden vom Linker an unterschiedlichen Orten im Datensegment abgelegt.

Zuerst die C-Lib-Daten (DATA), z.B. der String "Borland C++"... , dann die DATA Daten der einzelnen Objektdateien,
in der Reihenfolge des Linkens.
Anschließend kommen die BSS-Daten.
Wenn die Variablen jetzt initialisiert werden wechseln sie von der BSS- in die DATA-Section
und die Reihenfolge ist durcheinander.

Ich bleibe bei der alten Datei.
Da fehlen zwar noch die Adressen von den Funktionspointern für Reiseevents, Zaubersprüche, etc.,
welche ich aus der GCC-Variante übernehmen kann.

Ein größerer Aufwand muss dann bei den Beschreibungen der Schatztruhen betrieben werden,
da diese auch Funktionspointer enthalten.



Ja, das ist im Detail nicht so einfach wie gedacht, aber du hast meinen Respekt und Dank,
dass Du dich in dieses Projekt so gut einarbeitest. :thx:
Danke für deine Erklärungen! Ja, ich benutze die Skripte bc.sh und bc-ready.sh mit BCC 3.1. Bisher landet in der BLADEM.EXE kein Datensegment, glaube ich. Vielleicht muss ich das aber auch nochmal mit der aktuellsten Revision überprüfen.

(16.02.2017, 10:41)HenneNWH schrieb: Das mag als guter Stil erscheinen, aber initialisierte Daten (DATA) und uninitialisierte Daten (BSS) werden vom Linker an unterschiedlichen Orten im Datensegment abgelegt.
Nein, das hat mit Stil nichts zu tun. Das Problem ist, dass du umgekehrt Variablen in BSS kriegst, die eigentlich in DATA sein müssten, wenn du die Initialisierung überall weglässt. Was tust du dagegen? Meiner aktuellen Einschätzung nach, ist das ein größeres Problem - deswegen habe ich mich für die Variante mit Initialisierung entschieden.

(16.02.2017, 10:41)HenneNWH schrieb: Da fehlen zwar noch die Adressen von den Funktionspointern für Reiseevents, Zaubersprüche, etc.,
welche ich aus der GCC-Variante übernehmen kann.
Richtig: Die Funktionspointer fehlen noch und werden noch ein bisschen Arbeit abverlangen. Aber es sind nicht so wahnsinnig viele. Außerdem fehlen noch einige Pointer innerhalb des Datensegments.
(16.02.2017, 16:30)gaor schrieb:
(16.02.2017, 10:41)HenneNWH schrieb: Das mag als guter Stil erscheinen, aber initialisierte Daten (DATA) und uninitialisierte Daten (BSS) werden vom Linker an unterschiedlichen Orten im Datensegment abgelegt.

Nein, das hat mit Stil nichts zu tun. Das Problem ist, dass du umgekehrt Variablen in BSS kriegst, die eigentlich in DATA sein müssten, wenn du die Initialisierung überall weglässt. Was tust du dagegen? Meiner aktuellen Einschätzung nach, ist das ein größeres Problem - deswegen habe ich mich für die Variante mit Initialisierung entschieden.

Jetzt sehe und verstehe ich was du meinst. Alle Variablen vor Adresse DS:0xbc83 müssen initialisiert werden (auch wenn 0 drin steht),
da das alles DATA ist.
Ab Adresse DS:0xbc83 beginnt die BSS-Sektion, dessen Variablen nicht initialisiert werden dürfen.

Ich habe nach ein paar kleinen Änderungen und Initialisierungen schon einen beachtlichen identischen Bereich von 0x87 - 0x2988.
Das ist bedeutet, dass schon über ein Achtel der Variablen an seinem Platz sind. :)

Alles was vor Adresse 0x0094 steht kommt vom Compiler und kann verworfen werden.
An Adresse 0x94 habe ich char g_unkn_002 = 2; eingefügt und schon passt es. :) :) :)

(16.02.2017, 16:30)gaor schrieb:
(16.02.2017, 10:41)HenneNWH schrieb: Da fehlen zwar noch die Adressen von den Funktionspointern für Reiseevents, Zaubersprüche, etc.,
welche ich aus der GCC-Variante übernehmen kann.
Richtig: Die Funktionspointer fehlen noch und werden noch ein bisschen Arbeit abverlangen. Aber es sind nicht so wahnsinnig viele. Außerdem fehlen noch einige Pointer innerhalb des Datensegments.

Die Pointer im Datensegment dürfen richtige Pointer auf Bit8u* (also unsigned char*) sein.
Zumindest diejenigen, die sich nicht im Spielstand befinden.
Diese sollten weiterhin erstmal den Typ RealPt behalten.
Für diese muss dann einzeln entschieden werden wie mit ihnen verfahren wird, damit das Spiel weiterhin funktioniert.
In meinem Pull Request hatte ich mich jetzt übrigens dafür entschieden, gewöhnliche Variablendeklarationen statt ein struct (wie von llm vorgeschlagen) zu verwenden. Das liegt daran, das mit dieser Version das komplette DATA Segment identisch mit dem Original ist. Es scheint mir unnötig, angesichts dessen noch irgendwelche assertions und memcmp Checks einzubauen. Bis auf ein paar Pointer ist das Thema also jetzt durch.
Wie kann ich die Konstanten aus der common.h (z.B. für Items) in der datseg.cpp verwenden? Einfach nur die common.h einbinden geht nicht. Muss ich noch die v302de.h einbinden oder was genau ist da die beste Lösung?
Hey. Auf meinem neuen Rechner habe ich gerade wieder die Build-Umgebung für BrightEyes zusammengestellt und einen Test-Build durchgeführt. Leider kommt es zu zu Fehlern. Habe ich da doch noch etwas falsch einstellt? Wobei es für mich als Laien nach einem anderem Problem aussieht. BuildLog im Anhang.


.zip   BuildLog.zip (Größe: 34,09 KB / Downloads: 5)
--------
Warnung! Geschichte kann zu Einsichten führen und verursacht Bewusstsein!
Avatar by: Keven Law (CC BY-SA 2.0)
Hallo zusammen! Auch wenn so wie ich das sehe schon länger nichts mehr an Bright Eyes für Sternenschweif gemacht worden ist, will ich mal kurz einen Grafik-Bug posten, der dort im Sumpf des Vergessens auftritt. Unter Bright Eyes ist hier nämlich der Himmel statt neblig weiß senkrecht schwarz-weiß gestreift. Anbei zwei Vergleichsbilder.

       

Im Übrigen noch mal vielen Dank an alle fleißigen Mitwirkenden an Bright Eyes!! :respect::thx:

Wäre echt toll, wenn vielleicht jemand (Hendrik?) mal ein paar weitere Logger-Ausgaben für Sternenschweif implementieren könnte. Z.B. Ausgabe der Kampf-ID oder einen Kampfrunden-Counter (gibt es beides bei Schick) oder anderes. Sowas wäre sehr hilfreich! ;)
Gibt es überhaupt eine Kampf-ID in Sternenschweif wie bei Schick? *grübel*
"Save early and save often!" - Speichere oft und speichere früh! - Ist eine alte Zockerweisheit.
Was genau meinst du mit Kampf-ID? Kämpfe sind ähnlich wie in Schick, ja, einige sind aber auch in der Schweif.exe versteckt, und wir haben noch nicht alle lokalisiert.

Oder meintest du etwas anderes?
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: 2 Gast/Gäste