Startseite » Peering Inside Restricted Area - Savegame Analyse

Computerspiele, Games - Restricted Area - Savegame Analyse

Weitergehende Analyse der Savegame-Datei von Restricted Area


Bisherige Artikel zum Thema

Einleitung

Aufgrund des grossen Feadbacks zu meinem ersten kurzen Abriss über das Speicherformat von Restricted Area habe ich mich entschieden, etwas tiefer auf die anderen Werte in der Savegame-Datei einzugehen.
Ziel soll es sein, das Savegame weitestgehend zu untersuchen und alle wichtigen Offsets zu erkennen, sowie einen Struct zu entwerfen, der von einem Programm benutzt werden kann um den Spielstand zu bearbeiten.

Einzelschritte

  1. Besorg euch einen Hexeditor. Zum Beispiel y0das 16Edit FX ( unter Code Snippets ), der ist kostenlos und funktioniert wunderbar.
  2. Startet das Spiel und schreibt euch alle Eigenschaften eures Charakters auf.
  3. Startet den Windows Taschenrechner calc.exe ( wissenschaftliche Ansicht ) und rechnet die Werte in Hexadezimal um. Beachtet die reversen Byteorder der Daten! ( Siehe dazu auch das erste Tutorial. )
  4. Legt eine Sicherheitskopie von \Restricted Area\Save\CHARAKTER\CHARAKTER.sav an.
  5. Öffnet \Restricted Area\Save\CHARAKTER\CHARAKTER.sav mit dem Hexeditor eurer Wahl und sucht nach den aufgeschriebenen Werten.

Auswertung

Mit etwas suchen und wiederholtem Ändern der Werte kommen wir zu untenstehendem Schema.
0x0000 DWORD Das aktuelles Level des Charakters. 0x020c DWORD Die aktuellen Erfahrungspunkte. 0x0210 DWORD Die benötigte Erfahrung bis zum nächsten Level. 0x0214 DOWRD Unbekannter Wert. Die folgenden Werte sind zwar als DWORD gespeichert, können aber nur bis maximal 0xFF ( 255 ) gesteigert werden. Eine weitere Steigerung ist nur über Items im Spiel möglich. 0x0218 DWORD Stärke 0x021C DWORD Geschick 0x0220 DWORD Konstitution 0x0220 DWORD Intelligenz 0x0228 DWORD Reaktion 0x022C DWORD Willenskraft 0x0230 CHAR[] Der Name des Charakters, er darf maximal 17 Zeichen lang sein und ist mit 0x0D 0x0A terminiert. Relativ zu der Länge des Namens sind die Folgenden Werte gespeichert. Ist unser Spielername Mantis, 5 Zeichen Länge, so berechnet sich die Start-Offset des folgenden Blocks wie folgt:
Sei
b=0x0002 ( die Länge des Terminators )
c=0x0006 ( Länge des Charakternamens "Mantis" )
Geld Offset 0x00000230 + b + c
Offset in Zahlen 0x00000230 + 0x02 + 0x06 = 0x00000238 0xXX DWORD Ausdauer 0xXX DWORD Leben 0xXX DWORD Toleranz 0xXX DWORD Energie 0xXX DWORD Belastbarkeit 0xXX DWORD Schlag 0xXX DWORD Toxin 0xXX DWORD Geld 0xXX DWORD Welcher Charakter ( 04:Jessica, 03:Victoria, 02:Kenji, 01:Johnson ) 0x0730 DWORD Anzahl der zu vergebenden Skillpunkte. 0x0734 DWORD Anzahl der zu vergebenden Eigenschaftspunkte.

Code

Nun wollen wir die gefundenen Informationen in einen Struct zusammenfassen, den man benutzen kann um die Informationen strukturiert in ein Programm einzulesen. Hierbei befassen wir uns zunächst mit den Offsets 0x020C bis 0x0230. typedef struct __restricted_area_savegame_part1 { /// 0x020C DWORD dwCurrentExperience; /// Die aktuellen Erfahrungspunkte. DWORD dwNeededExperience; /// Die benötigten Erfahrungspunkte bis zum nächten Level. DOWRD dwUnknowen; /// unbekannt DWORD dwStrength; /// Stärke DWORD dwGeschick; /// Geschick DWORD dwCostitution; /// Konstitution DWORD dwIntelligence; /// Intelligenz DWORD dwReaction; /// Reaktion DWORD dwWill; /// Willenskraft } Nun folgt der Spielername, diesen können wir leider nicht in einen Struct lesen, da er aus unerfindlichen gründen von der Eingabe her zwar auf 17 Zeichen beschränkt ist, dass Feld in der Savegame-Datei jedoch leider dynamisch wächst. Unschön aber lösbar. Wir lesen einfach solange Bytes in eine Variable, bis wir auf den Terminator treffen.
Nach diesem etwas unschönen Intermetzo können wir wieder einen ordentlichen Struct definieren. typedef struct __restricted_area_savegame_part2 { DWORD dwConstituion; /// Ausdauer DWORD dwLife; /// Leben DWORD dwToleranz; /// Toleranz DWORD dwErnergie; /// Energie DWORD dwBelastbarkeit; /// Belastbarkeit DWORD dwSchlag; /// Schlage DWORD dwToxin; /// Toxin DWORD dwMoney; /// Geld DWORD dwCharactertype; /// Welcher Charakter ( 04:Jessica, 03:Victoria, 02:Kenji, 01:Johnson ) } Diese beiden Struct definieren die Grundwerte des Charakters. Diese kann man mit dem folgenden kleinen Dumper ansehen aber nicht ändern. Der Code bildet eine gute Grundlage um einen Editor zu erstellen und sollte mit den beiden Artikeln selbsterklärend sein. /// /// Dateiname: restricted-area-savegame-dumper.cpp /// /// Diese Datei gehört zu dem Artikel: /// - http://www.naden.de/public/articles/restricted-area-savegame-analyse.php /// /// Dieser Code ist (c) 2004 naden.de - Der Code darf frei gelesen, empfohlen, verlinkt, compiliert und /// teilweise in anderen Programmen - nichtkommerzieller Art - verwendet werden, solange die Quelle angegeben /// wird. Eine darüber hinausgehender Nutzung welcher Art und Weise auch immer bedarf der schriftlichen Genehmigung /// des Autors. Bei Zuwiderhandlungen ist der Schuldige damit einverstanden, Schadenersatzforderungen nachzukommen. /// #include <stdio.h> #include <fcntl.h> #include <io.h> /// def., denn dann müssen wir nicht windows.h importieren #define DWORD long #define UINT unsigned int /// Import muss nach den #defines kommen #include "restricted_area_types.h" /// /// Kleine Hilfsfunktion, die die geg. Werte auf neg. testet. /// RA schreibt, wenn ein Wert nicht gesetzt ist, auch nicht 0 ist, 0xffffffff /// void m_printf( char *szDesc, DWORD dwValue ) { printf( "\n%s: ", szDesc ); if( dwValue == 0xffffffff ) { printf( "-" ); } else { printf( "%u", dwValue ); } } int main( int argc, char ** argv ) { printf( "\nResticted Area - Savegame-Dumper (c)2004 www.naden.de - v0.1\n" ); int hFile; /// Datei zum lesen öffnen if ( ( hFile = open( argv[ 1 ], O_RDONLY ) ) < 0 ) { printf( "Syntax: %s path\filename.sav\n", argv[ 0 ] ); return( 1 ); } /// Gehe zu Offset 0x020C lseek( hFile, 0x020C, SEEK_SET ); /// Lese den ersten Struct ein _read( hFile, &ra_savegame_p1, sizeof( ra_savegame_p1 ) ); /// Lese den Charaktername ein char szCharactername[ 19 ]; UINT uIndex = 0U; while( _read( hFile, ((char*)szCharactername+uIndex), 1 ) > 0 ) { if( szCharactername[ uIndex ] == '\n' ) { break; } uIndex ++; } szCharactername[ uIndex ] = 0; /// Lese den zweiten Struct ein read( hFile, &ra_savegame_p2, sizeof( ra_savegame_p2 ) ); /// und den dritten read( hFile, &ra_savegame_p3, sizeof( ra_savegame_p3 ) ); close( hFile ); /// gebe die Daten auf dem Schirm aus printf( "\nCharaktername: %s", szCharactername ); printf( "\nCharaktertype: %u ( %s )", ra_savegame_p2.dwCharactertype, __restricted_area_charakters[ ra_savegame_p2.dwCharactertype-1 ] ); printf( "\nAktuelle Erfahrungspunkte: %u", ra_savegame_p1.dwCurrentExperience ); printf( "\nBenötigte Erfahrungspunkte bis zum nächten Level: %u", ra_savegame_p1.dwNeededExperience ); m_printf( "Unbekannt", ra_savegame_p1.dwUnknowen ); m_printf( "Stärke", ra_savegame_p1.dwStrength ); m_printf( "Geschick", ra_savegame_p1.dwSkill ); m_printf( "Konstitution", ra_savegame_p1.dwCostitution ); m_printf( "Intelligenz", ra_savegame_p1.dwIntelligence ); m_printf( "Reaktion", ra_savegame_p1.dwReaction ); m_printf( "Willenskraft", ra_savegame_p1.dwWill ); m_printf( "Ausweichen", ra_savegame_p2.dwDodge ); m_printf( "Widerstand", ra_savegame_p2.dwResistence ); m_printf( "Leben", ra_savegame_p2.dwLife ); m_printf( "Toleranz", ra_savegame_p2.dwToleranz ); m_printf( "Energie", ra_savegame_p2.dwErnergie ); m_printf( "Balistik", ra_savegame_p2.dwBalistik ); m_printf( "Schlag", ra_savegame_p2.dwHit ); m_printf( "Toxinresistenz", ra_savegame_p2.dwToxinResistence ); m_printf( "Geld", ra_savegame_p2.dwMoney ); /// Gehe zu Offset 0x020C lseek( hFile, 0x0730, SEEK_SET ); m_printf( "Zu vergebenden Entwicklungspunkte", ra_savegame_p3.dwDevelopmentpointsToUse ); m_printf( "Zu vergebenden Grundpunkte", ra_savegame_p3.dwCommonPointsToUse ); return( 0 ); } /// /// Dateiname: restricted-area-savegame-dumper.cpp /// /// Diese Datei gehört zu dem Artikel: /// - http://www.naden.de/public/articles/restricted-area-savegame-analyse.php /// /// Dieser Code ist (c) 2004 naden.de - Der Code darf frei gelesen, empfohlen, verlinkt, compiliert und /// teilweise in anderen Programmen - nichtkommerzieller Art - verwendet werden, solange die Quelle angegeben /// wird. Eine darüber hinausgehender Nutzung welcher Art und Weise auch immer bedarf der schriftlichen Genehmigung /// des Autors. Bei Zuwiderhandlungen ist der Schuldige damit einverstanden, Schadenersatzforderungen nachzukommen. /// typedef struct __restricted_area_savegame_part1 { /// 0x020C DWORD dwCurrentExperience; /// Die aktuellen Erfahrungspunkte. DWORD dwNeededExperience; /// Die benötigten Erfahrung bis zum nächten Level. DWORD dwUnknowen; /// unbekannt DWORD dwStrength; /// Stärke DWORD dwSkill; /// Geschick DWORD dwCostitution; /// Konstitution DWORD dwIntelligence; /// Intelligenz DWORD dwReaction; /// Reaktion DWORD dwWill; /// Willenskraft } __restricted_area_savegame_part1_type; typedef struct __restricted_area_savegame_part2 { DWORD dwDodge; /// Ausweichen DWORD dwLife; /// Leben DWORD dwToleranz; /// Toleranz DWORD dwErnergie; /// Energie DWORD dwBalistik; /// Balistik DWORD dwResistence; /// Widerstand DWORD dwHit; /// Schlage DWORD dwToxinResistence; /// Toxinwiderstand DWORD dwMoney; /// Geld DWORD dwCharactertype; /// Welcher Charakter ( 04:Jessica, 03:Victoria, 02:Kenji, 01:Johnson ) } __restricted_area_savegame_part2_type; typedef struct __restricted_area_savegame_part3 { /// 0x0730 DWORD dwDevelopmentpointsToUse; ///Anzahl der zu vergebenden Skillpunkte. /// 0x0734 DWORD dwCommonPointsToUse; /// Anzahl der zu vergebenden Eigenschaftspunkte. } __restricted_area_savegame_part3_type; char __restricted_area_charakters[ 4 ][ 10 ] = { { "Johnson\0" }, { "Kenji\0" }, { "Victoria\0" }, { "Jessica\0" } }; __restricted_area_savegame_part1_type ra_savegame_p1; __restricted_area_savegame_part2_type ra_savegame_p2; __restricted_area_savegame_part3_type ra_savegame_p3;

Zusammenfassung

Bei der durchsicht der Savegame-Datei sind mir noch viele andere Werte aufgefallen. Da ich aber nicht alle immer zu 100% zuordnen konnte habe ich erst einmal alles weggelassen, was nicht immer eindeutig zu bestimmen war. Je nachdem, ob dieser Artikel auf so viel Interesse wie der erste stösst, reichte ich gerne neue Informationen nach.

Begriffserkärungen

Weiterführende Links


Danksagungen

Danke allen Lesern, von denen ich so viel positives Feadback bekommen habe. Danke auch an ROA für die Beisteuerung einiger Offsets.

Anregungen, konstruktive Verbesserungsvorschläge und allgemeines Feadback sind wie immer willkommen. Bitte keine Anfragen zu gepatchten Savegames. Ich werde definitv keine Binaries veröffentlichen oder weitergeben. Jeder sollte mit den zwei Artikeln selber in der Lage sein, seinen Spielstand zu verbessern.

Der direkte Link zu diesem artikel lautet: http://www.naden.de/public/articles/restricted-area-savegame-analyse.php

Alle Angaben ohne Anspruch auf Richtigkeit oder Vollständigkeit. Die Manipulation der Dateien erfolgt auf eigene Verantwortung. Es ist ausgeschlossen, dass der Autor für etwaige Schäden am System oder den Programmen zur Rechenschaft gezogen werden kann.
Alle Markenrechte liegen bei den jeweiligen Eigentümern. Der Autor ist in keiner Weise mit den Herstellern oder dem Distributor des Spiels verbunden.

Artikeldatum: 19.10.2004
© 2004 naden.de - Der Artikel darf frei gelesen, empfohlen und verlinkt werden. Eine darüber hinausgehender Nutzung welcher Art und Weise auch immer bedarf der schriftlichen Genehmigung des Autors. Bei Zuwiderhandlungen ist der Schuldige damit einverstanden, Schadenersatzforderungen nachzukommen.