Themabewertung:
  • 1 Bewertung(en) - 5 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Reverse Engineering der NLT II
Ich finde die Schrift im ersten Bild am angenehmsten zu lesen.

Ist das Zufall oder gewollt, dass im Nachbau die Zeilen mit dem jeweils gleichen Wort des Originals enden?
"Alrik war durstig und hat getrunken."
Zitieren
Ich denke, das ist technisch bedingt - genau das würde sich mit einem "eigenen" Zeilenumbruch ja ändern, womit man die Textbox effizienter und sinnvoller befüllen könnte.
Zitieren
Danke für eure Ideen, ich bin ganz eurer Meinung. Es wäre zwar möglich, den Text dem Original sehr ähnlich zu gestalten, doch würde das an Lesbarkeit kosten. Also besser wir nehmen Schriftschnitt Medium, normale Leerzeichen, und etwas kleineren Text, bzw. größeren Zeilenabstand - im Original können zwei Buchstaben 'verschmelzen', wenn bspw. ein 'g' über einem 'i' steht. Um gewisse Zeilenabstände zu gewährleisten, müsste man die Textboxen aber selbst malen, auch um einen eigenen Textfluss zu ermöglichen:

(03.11.2023, 11:17)aeyol schrieb: Ich denke, das ist technisch bedingt - genau das würde sich mit einem "eigenen" Zeilenumbruch ja ändern, womit man die Textbox effizienter und sinnvoller befüllen könnte.

Genau, aktuell habe ich nur den Befehl für das Zeichnen von Textketten ersetzt. Der nächste Schritt wäre, den Befehl zum Malen der Textboxen anzupassen. So könnte man längere Texte selbstständig umbrechen und die Größe der Textboxen entsprechend anpassen. Das sollte subtil genug sein, um nicht aufzufallen – ob eine Box nun 5 oder 7 Zeilen hat, dürfte kaum jemanden stören. Hauptsache, die Texte bleiben gut lesbar und die Box sieht nicht fehl am Platz aus. Ein zusätzlicher, bisher nicht genannter Vorteil wäre, dass ich die Textboxgrafiken nicht strecken müsste, wie man es im ersten Beispiel meines letzten Posts passiert ist.



Was PT Mono betrifft, werde ich das später ausprobieren, aber ich bezweifle, dass die Originalschriftart eine Monospace-Font ist. Besonders bei Buchstaben wie 'i' oder 't' sieht man die schmalere Breite – und umgekehrt beim Leerzeichen, wie weit es ist. Also scheint es, dass jeder Buchstabe seine eigene fixe Breite hat – aber noch nicht ganz so ausgefeilt wie bei modernen Schriftarten, die Abstände abhängig von den nebeneinanderstehenden Buchstaben anpassen (Stichwort 'Kerning').
Zitieren
Ich hatte mich kürzlich mal mit dem BOB-Dateiformat auseinander gesetzt, die offenen Fragen des Wikis klären können, und den Dateiaufbau zusammen gefasst. Zu Dokuzwecken poste ich es mal hier:

Ich habe mal ein Screenshot des ungewöhnlichen .BOB von Adran, das ich im Spiel nicht triggern konnte, beigefügt:

   
Zitieren
Mhh, ich weiß nicht wie ich das übersehen konnte, aber ich habe wohl etwas mühselig gezeigt, dass das "Neue Format", einfach das "Alte Format" mit einem vorangestellten Pre-Header ist. Ab dem 'Headerbeginn' sind beide Formate identisch.
Der Sinn dahinter erschließt sich mir noch nicht ganz. Vielleicht hat der Pre-Header das Abstimmen der Animationen etwas erleichtert?

@Shihan, falls du mich zum Wiki / Repo hinzufügen magst, kann ich die Artikel zu BoB, 3DM und einigen anderen Formate gerne ergänzen. Da kam jetzt über das letzte Jahr doch so einiges zusammen.

Ich glaube, wir haben dann schon fast alle Formate in Riva geknackt! Was fehlt, sind noch .MOV/.MOF für die Autobewegungen, was sich mit eigenen 'Aufnahmen' in der 1.62 Demo wohl leicht entziffern lässt, sowie 5 Formate von dem MODULEAUTOMAP (.ANN .APA .LST .MSK .MST), die aber übersichtlich aussehen. Ah und dann noch die Dialogformate .LXT und .XDF . Und zuletzt fehlt noch der Komprimierungsmodus 0x02 in AIF, den man noch ausmachen muss.

Ergänzung:

Ich hatte zuletzt noch Spaß mit dem Komprimierungsmodus 0x02 in AIF, der in Riva zwar nur von einem Bild benutzt wird, aber dann doch nachgegeben hat:
Es ist eine Form von Lauflängenkodierung (RLE).
Im Endeffekt muss man immer nur 1 Byte b lesen und entscheiden, ob dieses < 128 ist:
Fall I) b < 128 leitet eine Folge von b + 1 Bytes ein, b selbst nicht mitgezählt, die 1:1 in das Ergebnis kopiert werden.
Fall II) b >= 128 steht immer vor einem Farbcode c. c wird nun 257 - b mal in das Ergebnis geschrieben.

Anders ausgedrückt: Das komprimierte Bild ist also in Pakete b_0, p_0, b_1, p_1, b_2, p_2, b_3, p_3, ... unterteilt,
wobei jedes b_n die Länge von p_n diktiert, und besagt, ob man p_n entweder ein- oder mehrfach in das Ergebnis schreibt.

Auf Fall I) folgt wohl immer Fall II), außer eine Folge ist zu lang, aber dies kommt in dem einen Rivabild nicht vor.
Dass die Fallgrenze bei < 128 liegt, habe ich auch nur geraten, und könnte man nur verifizieren, falls dieser Komprimierungsmodus z.B. auch in Sternenschweif oder der Schicksalsklinge benutzt wird.
Zitieren
Hi cmfrydos,

bin nach wie vor absolut überwältigt, wie weit Du hier gekommen bist, nachdem ich den Reverse-Engineer-Hut an den Nagel gehängt habe. Grandios, chapeau dafür!
Ich habe Dich mal als Collaborator für das Wiki eingeladen. Sag Bescheid, wenn das klappt oder auch nicht klappt.

Bin lange weg gewesen, weil mein RL mich gewaltig beschäftigt hat. Bis auf ein paar Arbeiten an einem Factorio-Projekt (YAFC CE) habe ich auch wenig hobbymäßiges Entwickeln gemacht. Alles ein wenig eingeschlafen.

Aber wenn Du im Wiki ein paar Dinge nachpflegst, schau ich gerne drüber und gebe meinen Senf ab, wenn gewünscht.
Auch Feedback und / oder Hilfe bei Deinem RivaHDViewer kann ich gerne geben, wenn es was konkretes gibt, wo Du Input brauchst. Kann nicht garantieren, dass ich innerhalb weniger Stunden antworte, aber ich habe mir vorgenommen, hier wieder öfter reinzusehen.
Zitieren
Hi Shihan,

ich bin momentan auch sehr vom echten Leben eingenommen, aber ich hoffe, bald mal wieder Zeit fürs Reverse-Engineering und Modden an der Nordlandtrilogie zu finden. Ich freue mich darauf, die Dinge ins Wiki nachzutragen - danke, das mit der Einladung hat geklappt!

Ich bin bezüglich der Repositories zum HDViewer und zum Probenlogger etwas im Rückstand und sollte auch dort mal einen Push vorbereiten. Auf meinem Rechner hatte ich zuletzt viel experimentiert.

Zuletzt, das war noch im Frühjahr, hatte ich einen kleinen Eureka-Moment, als ich merkte, dass es beim Aufbau eines Open-Source-Klons gar nicht notwendig wäre, bottom-up zu arbeiten, also erst die Funktionen zu implementieren, die keine anderen Funktionen aufrufen, dann die, die nur die schon implementierten aufrufen, und so weiter. Man kann auch mit dem Main-Loop anfangen, der bei BrightEyes erst ganz zum Schluss fertiggestellt werden konnte. Um das zu bewerkstelligen, startet man quasi eine DOS-Emulation an genau der Stelle, an der die noch nicht implementierte Unterfunktion im Speicher startet. Das klappt auch praktisch ganz gut. Zuletzt hing ich nur daran, dass, wenn ich zu viele der originalen Funktionen durch meine ersetze, manchmal Endlosschleifen auftreten, da Interrupts nicht aufgerufen werden. Hier rätsel ich zurzeit daran, wie ich diese Interrupt-Mechanismen am besten ersetze. Oft ist es nur ein: f1 liest in Endlosschleife die globale Variable a aus, bis ein Zeit-Interrupt a auf false setzt - sollte eigentlich machbar sein, ich stehe nur auf dem Schlauch.

Code:
bool a;

void f1(){
    a = true;
    while(a){
        // do nothing
    }
}

void interrupt(){ // called by CPU interrupt
    a = false;
}
Zitieren
Schön, dass die Einladung geklappt hat. Dann freue ich mich auf Deine Infos :)

Der Ansatz, den Du da beschreibst, ist durchaus vielversprechend! KeeperFX (ein Klon von Dungeon Keeper) hat das auch so gemacht, bis alles ersetzt war.
Im Rahmen der Recherche nach diesem Ansatz bin ich mal auf dieses Tool gestoßen: https://github.com/OpenRakis/Spice86
Leider, leider, leider funktioniert das nur mit Real Mode, was bei Riva schlecht ist, da Riva ja Protected Mode nutzt.

Deshalb meine Frage: Wie hast Du das hinbekommen, dass Du Funktionen ersetzen kannst? In welcher Umgebung läuft das Ding dann, Dosbox?
Zitieren
Ich fange in der DosBox die Call-Befehle ab, schaue, ob ich eine C-Funktion dazu habe, und rufe dann diese stattdessen auf. Ich vermute, dass das recht ähnlich läuft wie bei BrightEyes. Mit dem Protected Mode hatte ich bisher keine großen Schwierigkeiten, man muss nur aufpassen, da je nach Modus die Operanden- und Adressweiten verschieden sind. Lustigerweise mache ich genau das, was Spice macht, nur bisher nur im Protected-Mode und mit einer C-Ausgabe. Ich jage den Dosbox-Disassembler über den Startpunkt einer Funktion, dieser zerstückelt mir den Byte-Code in die einzelnen Befehle, und diese schiebe ich durch ein Python-Skript, das mir äquivalenten DosBox-C-Code ausgibt.
Ich habe, glaube ich, etwa 99,9% des verwendeten Befehlssatzes implementiert, und damit > 90% des gesamten Riva-Bytecode.

Raus kommt dann so etwas wie das:
(Habe den Ausschnitt nur schnell kopiert, um den aktuellen Stand darzustellen. Die Funktion an sich ist vielleicht nicht so interessant, und generierter Code ist nie schön, aber vielleicht ein Startpunkt ;))

Code:
void f_1DB2B0()
{
    CPU_Push32(reg_ebx);
    CPU_Push32(reg_ecx);
    CPU_Push32(reg_esi);
    CPU_Push32(reg_edi);
    CPU_Push32(reg_ebp);
    reg_ecx = reg_eax;                            // 32,32,mov ecx,eax
    reg_esi = reg_edx;                            // 32,32,mov esi,edx
    reg_edx = mem_readd_inline(reg_eax + 0x003D); // 32,32,mov edx,[eax+003D]
    test_32(reg_edx, reg_edx);
    if (FLG_NE) {
        goto l1DB2C8;
    }
    reg_ebx = mem_readd_inline(reg_eax + 0x0031); // 32,32,mov ebx,[eax+0031]
    reg_edi = mem_readd_inline(reg_eax + 0x0039); // 32,32,mov edi,[eax+0039]
    goto l1DB2CD;
l1DB2C8:
    reg_edi = reg_edx;                            // 32,32,mov edi,edx
    reg_ebx = mem_readd_inline(reg_eax + 0x0035); // 32,32,mov ebx,[eax+0035]
l1DB2CD:
    reg_eax = mem_readd_inline(reg_ecx + 0x0004); // 32,32,mov eax,[ecx+0004]
    mem_writed_inline(reg_ecx + 0x0029, reg_esi); // 32,32,mov [ecx+0029],esi
    reg_ebp = mem_readd_inline(reg_eax);          // 32,32,mov ebp,[eax]
    test_xor_32(reg_edx, reg_edx);
    reg_edx = reg_edx ^ reg_edx;
    decide_call(mem_readd_inline(reg_ebp + 0x0044), std::nullopt); // call near dword [ebp+0044]
                                                                   // ABS
    reg_eax = mem_readd_inline(reg_ecx + 0x0004);                  // 32,32,mov eax,[ecx+0004]
    reg_ebp = mem_readd_inline(reg_eax);                           // 32,32,mov ebp,[eax]
    test_xor_32(reg_edx, reg_edx);
    reg_edx = reg_edx ^ reg_edx;
    decide_call(mem_readd_inline(reg_ebp + 0x0010), std::nullopt); // call near dword [ebp+0010]
                                                                   // ABS
    reg_eax = mem_readd_inline(reg_ecx + 0x0004);                  // 32,32,mov eax,[ecx+0004]
    reg_ebp = mem_readd_inline(reg_eax);                           // 32,32,mov ebp,[eax]
    reg_edx = reg_edi;                                             // 32,32,mov edx,edi
    decide_call(mem_readd_inline(reg_ebp + 0x0024), std::nullopt); // call near dword [ebp+0024]
                                                                   // ABS
    reg_eax = mem_readd_inline(reg_ecx + 0x0004);                  // 32,32,mov eax,[ecx+0004]
    test_xor_32(reg_ebx, reg_ebx);
    reg_ebx = reg_ebx ^ reg_ebx;
    test_xor_32(reg_edx, reg_edx);
    reg_edx = reg_edx ^ reg_edx;
    reg_edi = mem_readd_inline(reg_eax);                           // 32,32,mov edi,[eax]
    reg_bx  = mem_readw_inline(reg_esi + 0x0012);                  // 16,32,mov bx,[esi+0012]
    reg_dx  = mem_readw_inline(reg_esi + 0x0014);                  // 16,32,mov dx,[esi+0014]
    decide_call(mem_readd_inline(reg_edi + 0x0058), std::nullopt); // call near dword [edi+0058]
                                                                   // ABS
    reg_eax = mem_readd_inline(reg_ecx + 0x0004);                  // 32,32,mov eax,[ecx+0004]
    reg_edx = mem_readd_inline(reg_eax);                           // 32,32,mov edx,[eax]
    decide_call(mem_readd_inline(reg_edx + 0x0028), std::nullopt); // call near dword [edx+0028]
                                                                   // ABS
    reg_ebx = mem_readd_inline(reg_ecx + 0x003D);                  // 32,32,mov ebx,[ecx+003D]
    test_32(reg_ebx, reg_ebx);
    if (FLG_E) {
        goto l1DB31F;
    }
    reg_eax = reg_ebx; // 32,32,mov eax,ebx
    CPU_Push32(-1);
    f_1CA710();
    CPU_Pop32();                                     // CALL // call 001CA710 ($-10c08) REL
    mem_writed_inline(reg_ecx + 0x003D, 0x00000000); // 32,32,mov dword [ecx+003D],00000000
l1DB31F:
    reg_eax = mem_readd_inline(reg_ecx + 0x0039); // 32,32,mov eax,[ecx+0039]
    CPU_Push32(-1);
    f_1CA710();
    CPU_Pop32();                                     // CALL // call 001CA710 ($-10c17) REL
    mem_writed_inline(reg_ecx + 0x0039, 0x00000000); // 32,32,mov dword [ecx+0039],00000000
    mem_writed_inline(reg_ecx + 0x0035, 0x00000000); // 32,32,mov dword [ecx+0035],00000000
    reg_eax = mem_readd_inline(reg_ecx + 0x0035);    // 32,32,mov eax,[ecx+0035]
    mem_writed_inline(reg_ecx + 0x0031, reg_eax);    // 32,32,mov [ecx+0031],eax
    reg_ebp = CPU_Pop32();
    reg_edi = CPU_Pop32();
    reg_esi = CPU_Pop32();
    reg_ecx = CPU_Pop32();
    reg_ebx = CPU_Pop32();
    return; // ret
}

Als nächstes wollte ich per Graphenanalyse die `goto`s durch `if`s, `while`s und `switch`-Statements ersetzen und mit ähnlicher Analyse eventuell die globalen Register durch lokale Variablen. Das war bisher nur eine Spielerei, und ich weiß gar nicht, was das Ziel ist. Die Performance im DosBox dynamic-cpu Mode (JIT-Übersetzung von DOS-ASM in x64) ist fantastisch, und der Source-Code von Riva scheint ja noch in den Händen von Attic zu sein und nicht verloren, wie bei der Schicksalsklinge. Für stellenweise Analysen, unter anderem, da ich so Breakpoints von Visual Studio aus setzen und interessante Stellen kommentieren kann, ist es aber praktisch.
Zitieren
Klingt richtig gut!

Vielleicht will man nicht unbedingt den ganzen Source so ersetzen. Aber falls man bei einer Neuimplementierung nicht genau weiß, wie etwas abläuft, oder Fehler suchen und fixen will, da könnte sowas schon echt hilfreich sein. Vor allem, wenn man dann größere Teile von Riva mit dem VS-Debugger durchlaufen kann. Das wäre ein Traum!

Könntest Du sowas hier gebrauchen:
   

Das ist noch aus meiner Analyse mit Ghidra. Die Offsets sind zwar verschoben, weil ich nur den LE-Teil der Exe analysiert habe, aber das sollte kein großes Problem darstellen.
Wenn Du damit was anfangen könntest, will ich mal sehen, wie ich die Daten exportiert bekomme.


Hast Du denn Pläne, die nachgebauten C-Funktionen zu teilen?
Zitieren
Das ist tatsächlich interessant, da ich noch nicht herausfinden konnte, wo die ALF-Extraktion stattfindet. Besonders interessant wäre es, die Funktion zu finden, die die Adressen zu den im RAM entpackten Dateien zurückgibt. Damit könnte man vielleicht auch die Funktionen zum Importieren der Spieldateien (für Bilder, Videos oder andere Dateien) finden, um hier noch offene Fragen zu klären. Wenn ich deine Grafik richtig lese, liegen die am Anfang des Programmes? Ich frage mich, wie Ghidra das den Quelldateinamen zuordnet, ich habe zwar textuelle Verweise im Hex gesehen, aber bei meinem Disasemblierverfahren sind die verloren gegangen.. :think: 

Groß händisch leicht lesbar nachgebaut habe ich bisher noch nichts. Am ehesten noch die Zufallsfunktionen, aber auch die noch nicht vollständig. Wenn da etwas fertig wird, teile ich das gerne. Bei den anderen ~350.000 Zeilen rein maschinell generierten Codes fühle ich mich nicht wohl, diese bereitzustellen, da es Attics Rechte tangieren könnte und weil da die schöpferische Eigenleistung, oder wie man das auch nennen mag, noch fehlt. Ich wäre aber offen, meine Skripte und DosBox zu teilen, damit könnte man sich das selbst generieren.
Zitieren
Wie gesagt, da ich den DOS/4GW-Stub am Anfang entfernt habe, um das Ding überhaupt laden zu können, stimmen die Offsets sicherlich hinten und vorne nicht. Aber ich könnte sicher die ersten N Bytes einer Funktion mit ausgeben, damit Du sie darüber zuordnen kannst. Mal sehen, der Python-Dialekt in Ghidra ist etwas eigenartig.

Die Namen der Dateien kommen von Debug-Ausgaben im Code. Viele, leider nicht alle, Funktionen/Methoden machen eine solche Debug-Ausgabe. Die läuft zwar anscheinend ins Leere, aber dabei gibt es immer wieder Verweise auf CPP-Dateien. Und mit einem anderen Python-Skript bin ich alle erkannten Funktionen durchgelaufen und habe geprüft, ob da eine solche Debug-Ausgabe existiert und dann, wenn dem so ist, den Dateinamen vor den Funktionsnamen gesetzt.


Wenn Dir nicht wohl ist, die Sachen zu teilen, ist das ok. Kann ich gut verstehen. Aber wenn es ein paar kleine Teaser gibt... da würde ich nicht nein sagen ;)
Zitieren
(29.07.2024, 10:01)Shihan schrieb: Aber ich könnte sicher die ersten N Bytes einer Funktion mit ausgeben, damit Du sie darüber zuordnen kannst.

Das wäre super! Dann könnte ich den Offset zu dir berechnen. Am besten von mehreren Stellen, denn wenn du eine absolute Adresse erwischst, könnte das bei mir nicht vorkommen, da diese beim Laden neu ausgerichtet werden. Mein Code wurde während der Runtime generiert (ich habe nur Funktionen, die während einer "Recording"-Session aufgerufen wurden), während dein Code statisch in der EXE liegt, so wie ich es verstanden habe. Da gibt es immer wieder Differenzen; in der EXE ist es, glaube ich, enger gepackt. Aber vielleicht kann Ghidra das umschalten?

Die Debug-Ausgaben, also auch Verweise auf die Dateinamen, liegen am Ende der EXE hintereinander. Ich konnte nur leider noch keine Stelle finden, die darauf per Offset zugreift und damit das Zugriffsmuster verstehen.
Zitieren
Bin noch nicht dazu gekommen, sorry. Chef und halbes Team ist im Urlaub und Kunde will Reklamationen durchboxen... Letzte Woche gab es fast 20 Überstunden...

Aber diese Woche ist wieder etwas Ruhe im Spiel. Mal sehen, ob ich das Export-Skrypt fertig bekomme.
Zitieren
Hallo Leute, :wave:

hab mich gerade wieder in Bright-Eyes eingearbeitet. Danke an Siebenstreich für die Beiträge!
Diese werde ich einarbeiten, sobald ich die Charaktergenerierung erfolgreich abgeschlossen habe.
Es ist nicht mehr viel Arbeit (zirka 30 Funktionen und der Audio-CD Teil)!

Aktuell habe ich etwas aufgeräumt und ansatzweise einheitliche Strukturen geschaffen.

Hier mal eine kleine Übersicht:
  • initialer Support für "Spirit of Adventure"
    (Die Datei VGAME muss in VGAME.EXE umbenannt werden, dann gehts!)
  • Bei GEN.EXE ist in der MIDI-Version das Timbre aktiviert (klingt runder und angenehmer)
  • Dokumentation für einige Entwicklerdinge
  • automatische Erkennung von Programmen, die mit Borland C++ 2.0, 3.0, 3.1 kompiliert wurden
    (Eye of Beholder 2 und 3, Dungeon Hack, Conan, ...)
  • automatische Erkennung von Programmen, die mit Borland Turbo-C und Turbo C++ kompiliert wurden
  • automatische Erkennung von einigen Bibliotheksfunktionen verschiedener BCC-Versionen
  • automatisches Testen von GEN.EXE (V1.05de) wie bei SCHICKM.EXE (V3.02de)
  • Entfernen der Unterstützung von QEMU zum automatischen Testen (DOSBox genügt)
  • Prinzipiell sind verschiedene Compilerversionen im virtuellen drive_c angedacht
  • Support für neuere GCC-Compiler

TODO:
  • GEN.EXE komplett fertigstellen
  • Patches von Siebenstreich integrieren
  • weiter aufräumen

Sonstiges:
Ich hab einen sehr interessanten Fork von DOSBox namens DOSBox-x gefunden. Mit diesem können viele Features zur Laufzeit per Menu umgestellt werden (CPU-Cores, Scaler, ...). :cool:

Frage an die Runde: Benutzt jemand noch VisualC++ zum Bauen von Bright-Eyes? Funktioniert das noch? :think:

VG Henne
Zitieren
Willkommen zurück Henne! Es freut mich, dass es dich "noch gibt" und dass du sogar noch Interesse an Bright-Eyes hast! :ok:
DOSBox-X habe ich zum Teil auch als Alternative zur normalen DOSBox verwendet, aber die ganzen Features habe ich nicht verwendet, bzw. nicht gekannt. :lol:

Mein letztes Kompilieren von Bright-Eyes ist schon ein paar Jahre her, deswegen habe ich keine Ahnung, ob und wie das aktuell funktioniert. :think:
--------
Warnung! Geschichte kann zu Einsichten führen und verursacht Bewusstsein!
Avatar by: Keven Law (CC BY-SA 2.0)
Zitieren
Auch von mir ein herzliches "Willkommen zurück", Henne!! :jippie:

Ich habe dank Bright Eyes Sternenschweif schon eine Menge verborgener Infos über die Spielmechaniken entlocken und hier posten können und werde es bestimmt auch in Zukunft weiterführen (aktuell bin ich in Riva unterwegs ;) ).

Vielleicht könntest du noch die Korrektur von 2 Bugs von Bright Eyes gegenüber dem Originalspiel auf deine ToDo-Liste setzen (falls du das nicht schon hast):

1. Den Grafik-Bug in den Sümpfen des Vergessen, durch den der komplette Hintergrund in schwarzen, vertikalen Streifen dargestellt wird.

2. Die falsche Implementierung der meisten Talentproben. Hier ist der entsprechende Thread dazu: https://www.crystals-dsa-foren.de/showth...p?tid=4404

Beides hat absolut keine zeitliche Priorität, ich möchte nur verhindern, dass es in Vergessenheit gerät. :)
Nochmal: Echt toll, dass du hier wieder an Bord bist und auch Bright Eyes weiter verbessern möchtest!!! Vielen Dank für deine bisherigen und zukünftigen Bemühungen!! :thx:
Zitieren
Danke für die freundlichen Worte, ihr Beiden.

@Tiefhusen: Mit Bright-Eyes und Riva hab ich auch schon einmal herumexperimentiert.
Allerdings wurde Riva mit einem komplett anderen Compiler (Watcom 10) übersetzt und ist 32-bittig.
Diesen Compiler hab ich mir schon auf EBay besorgt. ;-)
Sternenschweif und Riva haben bei mir aktuell eine gegen 0 konvergierende Priorität.

Im Moment hat GEN.EXE die höchste Priorität, da ich vor Jahren begonnen hatte dieses Programm ohne Softwaretests in die falsche Richtung zu entwickeln.
Aus Fehlern wird man klug, deshalb ist einer nicht genug!

Jetzt hab ich das so einheitlich umgesetzt, wie bei der SCHICKM.EXE und es geht zügig voran.
Es sind noch 28/132 Funktionen nachzubauen.

Einen kleinen Überblick über die verschiedenen Versionen gibt es unter folgender README.md

RANDOM FACT: Es existieren genau 32**1024 verschiedene Charakterbilder in Schick.
Beweis: Ein Bildpunkt kann genau 32 verschiedene Farben annehmen. Es existieren pro Bild genau 32*32 = 1024 Bildpunkte.
Somit ist die genaue Anzahl gleich:
187749072224295762487282918043534149672470009810028327446062532949263717\
368127024576140841104973120370273487261876951083004001413137204173750956938653\
211788724190430095984469913776932431963546640404661377521170242454281393564883\
698042160362597493239676179542430408230026967675408244369534225406182334053860\
953190851410763968250231766966368150031479733532494389362263966829774739549874\
576217702802049949175044144226916408271128525427622225198410553089064349578703\
883506197408833728032937541363391644479638264014861396658218947068985826257384\
271858030352807755971277360363293570350006795256116943835609813348656451703942\
739615910726879627516589755942615059584953695158906776349078531641699376974783\
981966272485654732492263213186492225477260675547523932337061020406120250964136\
034529347299464072163800076187742576595379686343865722042219212538664133431405\
598476618632378694390016986508065484388368263534489462021091442580691883449258\
543148763819608108278025227630151849488163230271017209333339572098874097605709\
683555074986308074644075465524908758151061239207358632374820522302308593867486\
159699800255775718113162926434961209248394655996108849613488899817872188299520\
363081282737595469502189721561285889897515363929727744544447417526634383587059\
070293805996935707713490568437981961300034126756863201261849257039580831538344\
714324593879688126002780304484145068997028656541324271928440299730361243738276\
658036052139964707237167826208674384719689501485461459019092511353744510977179\
559894717372061260467912691621997768268855590726394611504645144576

QED!
Zitieren
(23.03.2025, 01:17)HenneNWH schrieb: Hallo Leute, :wave:



hab mich gerade wieder in Bright-Eyes eingearbeitet. Danke an Siebenstreich für die Beiträge!

Diese werde ich einarbeiten, sobald ich die Charaktergenerierung erfolgreich abgeschlossen habe.

...

Frage an die Runde: Benutzt jemand noch VisualC++ zum Bauen von Bright-Eyes? Funktioniert das noch? :think:



VG Henne


Toll, dass du wieder zur Nordlandtrilogie zurückgefunden hast!
Die Arbeiten an Riva basieren zu einem großen Teil auf BrightEyes und dem Wiki – dafür ein herzliches Dankeschön! :jippie:

Bezüglich VisualC++: BrightEyes wird in Visual Studio 2022 mit einigen Anpassungen gebaut, allerdings kann ich nicht beurteilen, ob es semantisch äquivalente Executables wie die von g++ oder Borland erzeugt. Eine notwendige Änderung im Projekt besteht darin, datseg.cpp nur einmal einzubinden – beispielsweise über eine Präprozessordirektive. An die genauen Details erinnere ich mich leider nicht mehr.
Der Heldengenerator in Riva wurde weitgehend von der Schicksalsklinge übernommen, sodass der Code – auch wenn er mit Watcom kompiliert wurde – sehr ähnlich sein dürfte. Falls man deine reverse-engineerten C++-Funktionen mit Watcom kompiliert, könnte eventuell ein Bytecode entstehen, der in der Riva.exe wiederzufinden ist. Dein reverse-engineerter Code wäre somit sogar für Riva nützlich.
Ich muss gestehen, dass sich bereits vieles vom Schick-Code in Riva finden sollte – allerdings bin ich noch nicht dazu gekommen, dies im Detail zu überprüfen. Es wäre spannend, sich das einmal genauer anzuschauen.
Ich verfolge die Entwicklungen auf jeden Fall mit großem Interesse und würde mich freuen, wenn du auch Tipps dazu geben könntest, wie du beim Auffinden des Codes vorgehst – natürlich nur, wenn du möchtest.
Zitieren
@cmfrydos: Puh! Das zusammenzufassen ist nicht ganz einfach.

Vorab: Ich kann C/C++ und Assembler sehr gut lesen.

Damals hab ich angefangen nach dem Zufallszahlengenerator zu suchen,  da dieser von den Probewürfen aufgerufen wird.
Die Eingaben vom Zufallszahlengenerator habe ich mir vom Stack mit CPU_Pop16() heruntergeholt und ausgegeben.
Dann war mir klar von welcher Funktion die Probewürfe kommen, u.s.w.

Den Zufallszahlengenerator habe ich nachprogrammiert um mir auch das Ergebnis ansehen zu können.
Auf diese Art habe ich das Assemblerrefactoring erlernt.
Da ich jedoch auch Fehler mache habe ich meinen Code so gestaltet, dass er auch mit dem BCC kompilierbar wird und mir dafür eine Testumgebung gebaut.
Somit habe ich ein gutes Auge für BCC-Executables bekommen und kann da schnell "etwas sehen".
Mit "IDA 5.0" wurde mir auch die Struktur der EXE-Datei klar.

Wie du das jetzt mit dem Watcom-Code umsetzen kannst, weiß ich auch nicht.
Das sieht alles völlig anders aus und der IDA zeigt nur den Code der Executable an, in welche noch eine LE (Linear executable) eingebettet ist.
http://www.textfiles.com/programming/FORMATS/lxexe.txt

Such mal in der RIVA.EXE nach:
LE 00 00 00  00 00 00 00 02 00 01 00

Das ist der Anfang der eingebetteten LE-Datei, welche den tatsächlichen Code des Spiels enthält.
Dort hört mein Verständnis erstmal auf. Vielleicht hilft das. :toast: :rolleyes: :toast:
Zitieren




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