Automating the creation of a save game file

I am starting to build a force/scenario creation tool for a game. It will be in JS but I was thinking that it would be great to be able to take your created scenario and forces and export a Vassal game file for it.

When I looked at my local save games they all appear to be binary. Are there docs for creating a save game file from outside of Vassal?

No, and there are no docs for creating saves from inside Vassal, either.

There will be a save converter for V4, but it will go in the opposite direction—from the legacy V3 format to V4’s new format. It’s not likely anyone who knows the V3 code presently will author any new code that writes the V3 format.

If you look at the documentation for Decks, you will see they have the ability to save and load from an external file.

As a deck can hold any pieces, not just cards, you can (theoretically) use this to import pieces into a game. You would have to either build your force creation tool in VASSAL itself (having it put all the pieces in a deck and then saving them), or you might be able to reverse-engineer enough of the format of the external deck file to figure out how to create one using JS (good luck with that, though).

And, of course, this doesn’t help with creating new scenarios.

1 Like

Hi Joel

I am starting to look at a saved game converter for VASL. You mention that VASSAL4 will have a save converter. Is any work being done on this? Is there any opportunity to do something in parallel or use VASL as a test case?

Doug

I’ve already done some work for extracting data.

What are you trying to convert your saved games to?

From one version of vasl to the next. When we make counter changes or add functionality from one version to another, it is not picked up when games saved in previous versions are opened. Counters/prototypes are the biggest issue.

Right, that’s due to a design decision way back at the start that looked plausible at the time but turned out to be a bad idea.

Have you tried the refresher for updating saved games?

Yes, with mixed results. In some situations it worked and in others it did not. I have not done extensive enough testing to describe the results in detail but can certainly do so.

It is a bit awkward in that it will not work if there is an extension loaded and we make considerable use of those. I don’t know a player who doesn’t have any loaded. But of course they can be moved and then brought back in after conversion; not a game breaker but awkward for the average user.

That sounds like something which should be looked at for the refresher, in that case.

Hi,

My 2 ¢

vsav and vlog files are written in a particular format.

!VCSK<key><payload>

where <key> is a 2-byte (0-255) encryption key. This key is used to decode the rest of the <payload>. The key is encoded in two bytes c1 and c2 as

c1 = (enc_map[key & 0xF0] >> 4) 
c2 = (enc_map[key & 0x0F])

where enc_map maps a value to it’s hex character representation

0 -> 0, 1 -> 1, ..., 10 -> 'a', ..., 15 -> 'f'

Each character (8bit information) in <payload> is written as 2 bytes. A byte of information byte - as int - is written as

b  = byte ^ key
c1 = enc_map[(b & 0xF0) >> 4]
c2 = enc_map[(b & 0x0F)]

and c1 and c2 is written to the output stream.

To read back a byte of information, the inverse transformation is used

b1   = dec_hex(c1) << 4 
b2   = (dec_hex(c2)  ^ key) 
byte = (c1 | c2) & 0xFF

where

dec_hex: c -> c - dec_map[c]

and dec_map is

0x30 → 0x30, 0x31 → 0x30, …, 0x39 → 0x30
0x41 → 0x37, 0x42 → 0x37, …, 0x46 → 0x37
0x61 → 0x57, 0x62 → 0x57, …, 0x66 → 0x57

To read back the key, the same transformation is used, with key=0x0.

Before encryption of the <payload> it consists of “lines” of commands, separated by an escape character 0x27. In BNF

<payload> ::= <lines>
<lines> ::= 
             |  <line>
             | <line> 0x27 <lines>

The <line>s as commands to be interpreted by the CommandEncoder set on the module. To place a piece, for example, the line would be

+/<id>/<piece type>/<piece state>

where

  • <id> is a unique integer identifier - typically the current time with millisecond precision
  • <piece type> is all the trait types of the piece encoded
  • <piece state> is all the trait states of the piece encoded

To add a stack, a line would be

+/<id>/stack/<board>;<x>;<y>;<piece ids>;@@<layer>

where <x>,<y> are coordinates in the image of the <board> coordinate system. A move is

M/<piece id>/<new board>/<new x>/<new y>/<new over>/<old board>/<old x>/<old y>/<old over>/<player>

with the same meaning of <... board>, <... x>, <... y> as above, and <... over> is the piece ID which the moved piece is under, and <player> is the player id of the player making the move. A move is typically followed by a state update

D/<piece id>/<new piece state>

Other module components can also add <line>s to the save or log file. The PlayerRoster, for example, will write player definitions.

The <piece type> and <piece state> above are a little tricky. Each trait (including BasicTrait) of a piece defines a type, and possibly a state. Each traits type or state element values are written separated by ;. The values may contain some special characters which are then escaped by a \. The types and states of all traits are then assembled, separated by a \t' (TAB). The \tbetween the _n_'th and _n+1_'th (counting from 0) trait is prefixed by _n_`.

A vlog file is essentially a vsav file followed by a set of LOG commands. LOG commands are

 LOG\t<other command>

Here, <other command> is any of the commands issued by the module. For example, a chat entry is

LOG\tCHAT<text>

where <text> is the chat message. But <other command> can also be moves, state changes, and so on.

With this, one can write code that will generate a vlog or vsav file. I have a Python module for that you can find on GitLab. The module is really intended to take a PDF file containing images, one per page, and a JSON file with metadata per page (what is the image, size of image, whether to define zones, grids, regions, etc. in the image), and generate a (draft) module. When used like this, one can give a patch Python script that can manipulate the module, including adding a vsav file generated programmatically. For a somewhat complicated example see this Afrika Korps Print’n’Play and VASSAL module. Here, I generate the image PDF via LaTeX and my package wargame_tex.

A few thoughts on the messaging format used. Clearly the message format was designed so as to facilitate low-bandwidth network communication. However, I think there are some inherent problems with the format. It is quite easily broken by data (the message Bad data that you sometimes see in VASSAL). Also, since the entire type and state of a piece is written, and the piece constructed from that, it can cause issues with upgraded modules. That’s of course why there’s the VASSAL tool to update piece definitions. However, that only works after a vlog or vsav file has been loaded and traversed, which can be too late. I would suggest to use a more readily decodeable format - say JSON. To keep the network traffic down, one could simply transmit the parts of the JSON that needs updating. One could of course also use XML, but that tends to carry more overhead.

Anways, as I said in the beginning, my 2 ¢

Yours,
Christian

1 Like

That’s a good guess, but that’s not the actual reason. When Rodney Kinney designed the format in the mid 1990s, Java’s XML parsing was too slow to handle everything being XML, so as a workaround, Rodney devised the baroque encoding we still have today.

There are more than some. I’ve wanted to replace the file format almost since I first encountered it more than fifteen years ago.

The current plan is that V4 will use human-readable JSON for storing module and log data.

3 Likes