Homesource Forums

Homeworld Source Editing Talk
It is currently Mon Sep 25, 2017 10:51 pm

All times are UTC - 5 hours




Post new topic Reply to topic  [ 19 posts ] 
Author Message
PostPosted: Thu Dec 14, 2006 6:20 pm 
Offline
coder
User avatar

Joined: Tue Dec 14, 2004 3:24 pm
Posts: 324
Location: UK (UTC+0)
I'm about to commit a change to the single player mission tracking stuff that moves us away from an incrementing level number to an ordered list of missions. This is the first step towards being able to integrate the Raider Retreat mission into the main game. I still have a few bits and pieces to clean up (loaded save games aren't initialising their watchFunctions properly so the game sits idle) but I'm pretty close.

Anyway, this change will require a save game version bump so all your save games will become unplayable. :( I don't think I'll need to bump the version again after this but I can't be certain...

_________________
MacHomeworld | HomeworldSDL.org


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jan 02, 2007 5:44 am 
Offline
coder

Joined: Tue Nov 07, 2006 4:40 am
Posts: 236
Hi.

I'm wondering how easy it'd be to write a binary, or modify the code to process the old file versions? I guess it'd be purely processing the level information so I hope it'd be fairly simple to cater for the old version.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 15, 2007 7:20 pm 
Offline
coder
User avatar

Joined: Tue Dec 14, 2004 3:24 pm
Posts: 324
Location: UK (UTC+0)
I've just committed this in r548. I've put some backward compatibility code in so you should still be able to play your old save games. However I still needed to bump the version number so any save games created by new binaries will not be playable with old ones.

The short version of this change is that the numeric mission counter has been replaced with a mission sequence:

Code:
MissionEnum missionSequence[] = {
#if defined(HW_GAME_HOMEWORLD)
    MISSION_1_KHARAK_SYSTEM,                // *REQUIRED* - creates initial fleet
    MISSION_2_OUTSKIRTS_OF_KHARAK_SYSTEM,
    MISSION_3_RETURN_TO_KHARAK,
    MISSION_4_GREAT_WASTELANDS_TRADERS,
    MISSION_5_GREAT_WASTELANDS_REVENGE,
    MISSION_6_DIAMOND_SHOALS,
    MISSION_7_THE_GARDENS_OF_KADESH,
    MISSION_8_THE_CATHEDRAL_OF_KADESH,
    MISSION_9_SEA_OF_LOST_SOULS,
    MISSION_10_SUPER_NOVA_STATION,
    MISSION_11_TENHAUSER_GATE,
    MISSION_12_GALACTIC_CORE,
    MISSION_13_THE_KAROS_GRAVEYARD,
    MISSION_14_BRIDGE_OF_SIGHS,
    MISSION_15_CHAPEL_PERILOUS,
    MISSION_16_HIIGARA,
#elif defined(HW_GAME_RAIDER_RETREAT)
    MISSION_1_KHARAK_SYSTEM,
    MISSION_2_OUTSKIRTS_OF_KHARAK_SYSTEM,
    MISSION_3_RETURN_TO_KHARAK,
    MISSION_4_GREAT_WASTELANDS_TRADERS,
    MISSION_5B_TURANIC_RAIDER_PLANETOID,
#elif defined(HW_GAME_DEMO)
    MISSION_1_KHARAK_SYSTEM,
    MISSION_2_OUTSKIRTS_OF_KHARAK_SYSTEM,
#endif
};

Yes, this means you can flip missions around willy nilly. However, doing so may break the game in weird and wonderful ways. I'll still look into any bugs or issues you encounter when doing this but be warned that the canon mission order will get preferential investigation. Please note that if the first mission is not MISSION_1_KHARAK_SYSTEM, you won't get a fleet assigned and after the mission loads it will change to the Sensor Manager and zoom fully out - like I said, "break the game in weird and wonderful ways..."

Unfortunately, there is still a fair bit of work left to do before things are seamless. Most of it entails ripping out hardcoded mission references/mappings and replacing them with generic code which then gets called from the .kas script for the mission. This will make it easier to randomly juggle missions but also makes the engine more suitable for modding (e.g. enabling MISSION_10_SUPER_NOVA_STATION radio interference to work in any mission).

As a final thought, I thoroughly recommend that everyone play the following mission sequence at least once:

Code:
MISSION_1_KHARAK_SYSTEM,
MISSION_16_HIIGARA,

As everyone knows, all the intermediate missions in the game are just there to waste your time. If the Kushan people had just built a bigger hyperdrive engine they could have gone home in one jump and prevented the needless loss of life and destruction of private property that otherwise occurs. So do everyone a favour, board your ship of peaceful exploration, step through the magic blue door and say "Hi honey, I'm home(world)!"...

_________________
MacHomeworld | HomeworldSDL.org


Top
 Profile  
 
 Post subject: Anatomy of a SaveFile
PostPosted: Fri Mar 16, 2007 5:15 pm 
Offline
coder

Joined: Tue Nov 07, 2006 4:40 am
Posts: 236
Hi.

This is going to be a long post, so I'll try and break it down a bit.

It'll be a dissection of the beginning of a savefile, and also a suggestion about how we could bump the version without loosing the savefile. (I'm up to level 13 after stealing everything not nailed down and I don't wanna start again. :) )

Anyhow:
I've been going through a savefile just to see if I could understand it, and try to understand the format.

This is the hexdump of the beginning of one of my savegames.
Code:
00000000  28 00 ad 10 00 00 00 20  04 00 00 00 01 00 00 00  |(...... ........|
          |Version  | |<-      sp chunk.               ->|
                      |<-     ->| Info chunk
                                   |<-     ->|  Size of chunk
                                               |<-     ->| Data
00000010  00 00 00 20 04 00 00 00  0a 00 00 00 00 00 00 20  |... ........... |
          |<-      SongNumber chunk.       ->|
          |<-     ->| Info chunk
                      |<-     ->|  Size of chunk
                                   |<-     ->| Data
00000020  04 00 00 00 02 00 00 00  00 00 00 80 90 00 00 00  |................|
          numPlayers          ->|
                      |<-data ->|       There are two players in a single player game. :)

                                   |<- Player names.
                                   |<-     ->| Data type == BASIC_STRUCTURE
                                               |<-     ->|  Size of chunk (90 = 18 * )
                                                (MAX_MULTIPLAYER_PLAYERS * MAX_PERSONAL_NAME_LEN)
00000030  50 6c 61 79 65 72 00 00  00 00 00 00 00 00 00 00  |Player..........|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
* // Missing data (All null)
                                                       ->|
000000c0  00 00 00 20 04 00 00 00  00 00 00 00 00 00 00 20  |... ........... |
          |<-   Going to skip this, but it's three data chunks (12 bytes per data chunk )
                  multiplied by MAX_MULTIPLAYER_PLAYERS (8) gives 288 or 0x120
000000d0  04 00 00 00 8e 9f a1 ff  00 00 00 20 04 00 00 00  |........... ....|
000000e0  ff ff ff ff 00 00 00 20  04 00 00 00 01 00 00 00  |....... ........|
000000f0  00 00 00 20 04 00 00 00  ff d2 00 ff 00 00 00 20  |... ........... |
00000100  04 00 00 00 ff 00 00 ff  00 00 00 20 04 00 00 00  |........... ....|
00000110  00 00 00 00 00 00 00 20  04 00 00 00 00 64 ff ff  |....... .....d..|
00000120  00 00 00 20 04 00 00 00  d2 d2 c8 ff 00 00 00 20  |... ........... |
00000130  04 00 00 00 00 00 00 00  00 00 00 20 04 00 00 00  |........... ....|
00000140  3c ff 3c ff 00 00 00 20  04 00 00 00 d2 d2 c8 ff  |<.<.... ........|
00000150  00 00 00 20 04 00 00 00  00 00 00 00 00 00 00 20  |... ........... |
00000160  04 00 00 00 ff ff 3c ff  00 00 00 20 04 00 00 00  |......<.... ....|
00000170  d2 d2 c8 ff 00 00 00 20  04 00 00 00 00 00 00 00  |....... ........|
00000180  00 00 00 20 04 00 00 00  ff ff ff ff 00 00 00 20  |... ........... |
00000190  04 00 00 00 ff ff ff ff  00 00 00 20 04 00 00 00  |........... ....|
000001a0  00 00 00 00 00 00 00 20  04 00 00 00 ff 00 ff ff  |....... ........|
000001b0  00 00 00 20 04 00 00 00  d2 d2 c8 ff 00 00 00 20  |... ........... |
000001c0  04 00 00 00 00 00 00 00  00 00 00 20 04 00 00 00  |........... ....|
000001d0  00 c8 ff ff 00 00 00 20  04 00 00 00 d2 d2 c8 ff  |....... ........|
                                                       ->|
000001e0  00 00 00 20 04 00 00 00  01 00 00 00 00 00 00 20  |... ........... |
**** Important bit ****
          |<- data chunk                   ->|  singlePlayerGameInfo.currentMission
                                   |<-     ->|  The actual level you're on.
000001f0  04 00 00 00 ed de a5 f1  00 00 00 20 04 00 00 00  |........... ....|
00000200  02 80 00 00 00 00 00 20  04 00 00 00 00 80 00 00  |....... ........|


dataChunks are stored as the following (from SaveGame.h)
Code:
typedef sdword TypeOfSaveChunk;     // STRUCTURE | SAVE_xxx     e.g. BASIC_STRUCTURE|SAVE_UNIVERSE

typedef struct
{
    TypeOfSaveChunk type;
    sdword contentsSize;
    // chunk contents goes here
} SaveChunk;

so we have 12 bytes to store 1 sdword value, with the actual value in bytes 9-12.

The data is also endian, so this is why the savegames aren't transferable between OS's, but we should be able to write a converter. :D

As a very simple test, it should be possible to edit the file and just change this value to bring the level number into alignment with the new numbering. whilst it should work this way I'd only suggest it be tried on the save for the start of the level.

Hope the above makes sense. If not then let me know. :) :)

Aunxx


Top
 Profile  
 
PostPosted: Fri Mar 16, 2007 5:22 pm 
Offline
coder

Joined: Tue Nov 07, 2006 4:40 am
Posts: 236
Hi.

the following is kinda a proof of concept for bumping the version of the game but allowing the load of old save games.
Code:
Index: SaveGame.h
===================================================================
--- SaveGame.h  (revision 546)
+++ SaveGame.h  (working copy)
@@ -14,9 +14,11 @@
 #include "Types.h"
 
 #define SAVE_VERSION_NUMBER_ORIGINAL    0x10ad0027  // save game version for original Homeworld (Relic)
+#define SAVE_VERSION_NUMBER_HWSDL_OLD   0x10ad0028  // save game version for Homeworld SDL project
 #define SAVE_VERSION_NUMBER_HWSDL       0x10ad0028  // save game version for Homeworld SDL project
 
 #define SAVE_VERSION_NUMBER             SAVE_VERSION_NUMBER_HWSDL
+#define SAVE_VERSION_NUMBER_OLD         SAVE_VERSION_NUMBER_HWSDL_OLD
 
 #define BASIC_STRUCTURE                 0x80000000
 #define VARIABLE_STRUCTURE              0x40000000
@@ -145,6 +147,7 @@
 #define VERIFYSAVEFILE_OK               0
 #define VERIFYSAVEFILE_ERROROPENING     -1
 #define VERIFYSAVEFILE_BADVERSION       -2
+#define VERIFYSAVEFILE_OLDVERSION       -3
 
 sdword VerifySaveFile(char *filename);
 


and
Code:
Index: SaveGame.c
===================================================================
--- SaveGame.c  (revision 547)
+++ SaveGame.c  (working copy)
@@ -55,6 +55,7 @@
 GrowSelection BlobRegistry;
 filehandle savefile = 0;
 sdword savefilestatus = 0;
+sdword version = -1;
 
 /*=============================================================================
     Private Function prototypes
@@ -86,6 +87,7 @@
 
 extern sdword actorFlagsEnabled;
 
+
 /*=============================================================================
     Functions:
 =============================================================================*/
@@ -479,8 +481,8 @@
 
 void SaveVersionInfo(void)
 {
-    sdword version = SAVE_VERSION_NUMBER;
     FILE *fp;
+    version = SAVE_VERSION_NUMBER;
 
     dbgAssertOrIgnore(!fileUsingBigfile(savefile));
     fp = fileStream(savefile);
@@ -493,7 +495,7 @@
 
 sdword LoadVersionInfo(void)
 {
-    sdword version;
+//    sdword version;
     FILE *fp;
 
     dbgAssertOrIgnore(!fileUsingBigfile(savefile));
@@ -504,12 +506,17 @@
         return VERIFYSAVEFILE_ERROROPENING;
     }
 
-    if (version != SAVE_VERSION_NUMBER)
+    if (version == SAVE_VERSION_NUMBER)
     {
-        return VERIFYSAVEFILE_BADVERSION;
+        return VERIFYSAVEFILE_OK;
     }
 
-    return VERIFYSAVEFILE_OK;
+    if (version == SAVE_VERSION_NUMBER_OLD)
+    {
+        return VERIFYSAVEFILE_OK;
+    }
+
+    return VERIFYSAVEFILE_BADVERSION;
 }
 
 void SavePreGameInfo(void)
@@ -569,6 +576,8 @@
 
     if (sp)
     {
+// Could put something here testing the now defined version number to
+// process the currentMission to the correct value in the bumped version.
         singlePlayerGameInfo.currentMission = LoadInfoNumber();
     }
     else
@@ -710,11 +719,14 @@
 
     savefile = fileOpen(filename, FF_UserSettingsPath);
     verify = LoadVersionInfo();
-    if (verify != VERIFYSAVEFILE_OK)
+    if ((verify != VERIFYSAVEFILE_OK) && (verify != VERIFYSAVEFILE_OLDVERSION))
     {
         dbgFatalf(DBG_Loc,"Error %d loading game %s",verify,filename);
         return;
     }
+    if (verify == VERIFYSAVEFILE_OLDVERSION){
+        dbgMessagef("Attempting to load old savefile version.");
+    }
     LoadPreGameInfo();
 }


Anyway, just a suggestion, but it'll depend on how lmop implements the mission code, and if we want to allow the load of "previous" version saves.

Aunxx


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 16, 2007 7:16 pm 
Offline
coder
User avatar

Joined: Tue Dec 14, 2004 3:24 pm
Posts: 324
Location: UK (UTC+0)
I think you've either missed or misunderstood what I said here:

lmop wrote:
I've put some backward compatibility code in so you should still be able to play your old save games. However I still needed to bump the version number so any save games created by new binaries will not be playable with old ones.

and that I've already committed the code changes. Your current save games should still work. If they don't I'd be interested to know that - although replication might be a bit of a bugger because, as you say, the endian issues haven't yet been addressed so I won't be able to load your save games myself. Still, knowing there is an issue is the first step toward fixing it. Note, however, that your current binaries won't load games that are saved with the new code.

Your suggestion as to how to write the backward compatibility is pretty much how I did it. See src/Game/SaveGame.[ch]:

Code:
#define SAVE_VERSION_NUMBER_ORIGINAL    0x10ad0027  // save game version for original Homeworld (Relic)

#define SAVE_VERSION_NUMBER_HWSDL_1     0x10ad0028  // => (20031124) bump for Homeworld SDL project
#define SAVE_VERSION_NUMBER_HWSDL_2     0x10ad0029  // => (20061214) currentMission -> MissionEnum

// don't forget to update SaveGame.c: supportedVersionNumbers[] if you change this
#define SAVE_VERSION_NUMBER             SAVE_VERSION_NUMBER_HWSDL_2

Code:
// all the save game format versions this binary supports
sdword supportedVersionNumbers[] = {
    SAVE_VERSION_NUMBER_ORIGINAL,
    SAVE_VERSION_NUMBER_HWSDL_1,
    SAVE_VERSION_NUMBER_HWSDL_2,
};

Though having copied-and-pasted that, it occurs to me that SAVE_VERSION_NUMBER_ORIGINAL definitely isn't supported as it used to contain direct memory dumps from Windows so wouldn't work in a cross-platform environment. I'll remove it after I post this.

Code:
sdword LoadVersionInfo(void);

Code:
void LoadMissionNumber(void);

_________________
MacHomeworld | HomeworldSDL.org


Top
 Profile  
 
 Post subject:
PostPosted: Sat Mar 17, 2007 12:46 pm 
Offline
coder

Joined: Tue Nov 07, 2006 4:40 am
Posts: 236
lmop wrote:
I think you've either missed or misunderstood what I said here:


Yup. Totally missed that. :)

But I now know a hell of a lot more about the way Homeworld saves a game though. :)


Aunxx.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Mar 18, 2007 1:47 am 
Offline
Site Admin
User avatar

Joined: Tue Dec 14, 2004 12:41 am
Posts: 326
r548 is crashing immediately after loading some old savegames... but not others.

It seems more likely to happen the more ships are in the savegame.So it's not surpirising that the linux savegame....
http://homesource.nekomimicon.net/downl ... ighs_Ready
... triggers the bug.

The game loads, you see your ships, and then it crashes so I'd guess it's because some state is not being properly applied. after the ships are loaded. (boy, was that generic or what? :) )

I've not put it in the tracker yet as the code seems to be in flux?


Top
 Profile  
 
 Post subject:
PostPosted: Sun Mar 18, 2007 5:37 am 
Offline
coder

Joined: Mon Jan 29, 2007 2:45 pm
Posts: 61
I also have an old autosave in the beginning of Mission 7, The Garden of Kadesh. It loads without crashing, but the Kadeshi never appear.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Mar 18, 2007 1:01 pm 
Offline
coder
User avatar

Joined: Tue Dec 14, 2004 3:24 pm
Posts: 324
Location: UK (UTC+0)
Both sound like they might be related to the KAS functions not being mapped properly. In the case of the crash, attach a debugger and see what kills it.

_________________
MacHomeworld | HomeworldSDL.org


Top
 Profile  
 
 Post subject:
PostPosted: Sun Mar 18, 2007 3:04 pm 
Offline
Site Admin
User avatar

Joined: Tue Dec 14, 2004 12:41 am
Posts: 326
lmop wrote:
Both sound like they might be related to the KAS functions not being mapped properly. In the case of the crash, attach a debugger and see what kills it.


src/Game/KASFunc.c
Code:
sdword kasfShipsCount(GrowSelection *ships)
{
    return ships->selection->numShips;
}


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 22, 2007 3:52 pm 
Offline
coder

Joined: Mon Jan 29, 2007 2:45 pm
Posts: 61
Code:
svn diff -c548 src/Game/KAS.c
[...]
-    CurrentMissionWatchFunction = IndexToWatchFunction(LoadInfoNumber());
+    CurrentMissionWatchFunction = MissionEnumToWatchFunction((MissionEnum) LoadInfoNumber());

Weren't those indices zero-based? And MissionEnum is one-based?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 22, 2007 6:39 pm 
Offline
coder
User avatar

Joined: Tue Dec 14, 2004 3:24 pm
Posts: 324
Location: UK (UTC+0)
Good catch. That's a little disconcerting though as that means my save games aren't what I think they are...

I now remember why I hadn't committed this earlier on. It's a bit of a rabbit hole, the old KAS code. The integration/hardcoding of the generated mission and KAS code is tight. There goes my weekend. :(

_________________
MacHomeworld | HomeworldSDL.org


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 22, 2007 6:40 pm 
Offline
coder
User avatar

Joined: Tue Dec 14, 2004 3:24 pm
Posts: 324
Location: UK (UTC+0)
Actually I may be overthinking the problem as usual. Try your save games with r560.

_________________
MacHomeworld | HomeworldSDL.org


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 23, 2007 1:12 am 
Offline
Site Admin
User avatar

Joined: Tue Dec 14, 2004 12:41 am
Posts: 326
lmop wrote:
Actually I may be overthinking the problem as usual. Try your save games with r560.


Partial success. Bridge of Sighs Ready now loads without crashing (all 480+ ships 8) ), but other save games have problems like... no ships... (fun one, that... you are kicked into the sensors manager immediately because there are no ships to focus on, and you can't get back to the main screen long enough to bring up the Quit menu : )).

And the crash while going to a new game from a save game is still there.

Setting up the debug runs now and will post here when I have the parameters. Will also put this issue (or issues) in the tracker.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 23, 2007 12:03 pm 
Offline
coder

Joined: Mon Jan 29, 2007 2:45 pm
Posts: 61
And the Kadeshi appear as normal in mission 7.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 23, 2007 6:43 pm 
Offline
coder
User avatar

Joined: Tue Dec 14, 2004 3:24 pm
Posts: 324
Location: UK (UTC+0)
zapkitty wrote:
And the crash while going to a new game from a save game is still there.

Fixed in r562.

_________________
MacHomeworld | HomeworldSDL.org


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 23, 2007 8:13 pm 
Offline
coder
User avatar

Joined: Tue Dec 14, 2004 3:24 pm
Posts: 324
Location: UK (UTC+0)
lmop wrote:
Please note that if the first mission is not MISSION_1_KHARAK_SYSTEM, you won't get a fleet assigned

Having investigated this further, it's not actually the mission that's important but rather the first NIS sequence that is played by it (Homeworld.big/nis/n01r[12].script). In the NIS, a number of ships are created which then fly around for you entertainment. Some of these ships have the remainAtEnd keyword associated with them - in particular, a rather large Mothership and its accompanying seven scouts and resource collector. I find it a little disconcerting that an animation sequence is what actually specifies and instantiates the initial fleet composition...

_________________
MacHomeworld | HomeworldSDL.org


Top
 Profile  
 
 Post subject:
PostPosted: Sat Mar 24, 2007 10:49 am 
Offline
Site Admin
User avatar

Joined: Tue Dec 14, 2004 12:41 am
Posts: 326
Apparently all savegames now load properly, whether they are in the new format or in the previous format.

Re: SP initial fleet,

(Warning: crude ludgery ahead :) )

... can that remainAtEnd keyword be gotten around by having your initial fleet hyper in?

If so then for SP missions starting with static installations akin to the scaffold... perhaps a "psuedo-NIS" effect which focuses on something else while the installation et al is "appeared" ...

(The hyperspace sound should have a mute option in SP KAS though... I've always found it disconcerting to hear the Kadeshi invisibly hyper in while watching my fleet deploy at the start of "Gardens".)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 19 posts ] 

All times are UTC - 5 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group