I was hoping to use python to auto generate some scenario set-ups (.vsav files) for a module. The script could read the plain text in the game rules and then output a scenario file as plain text. Is this doable at all? Thanks
Yes, you could do it, but you’d have to get an understanding of the file format by reading the relevant code. I wouldn’t spend time doing this with V3, myself, but it’s up to you. We’re redesigning the file format for V4 to be human-readable.
Glad to hear you guys are working on making VSAV files human readable!
Opening up the savedGame part of a vsav file gives me a very long string of letters and numbers ie. f01060109010405…(on and on and on). Is this where any theoretical text changes would have to take place in version 3? You’re probably right that it’s not worth the time to mess around with then!
Yes, that’s the place. You’d need to run them through the deobfuscator first to see anything semi-readable.
Can you point me in the direction of the deobfuscator? Was expecting to find it in the vassal-master folder under tools but no sign of it and windows search is of no help as is usual.
VASSAL.tools.io.DeobfuscatingInpitStream, it’s not a stand-alone tool as such, thought it has a main() that will deobfuscate a file and write it to standard out. You won’t be happy with the output either ![]()
It is doable because I have done it. I have written my own tools in C# .NET to read/write .vsav files to/from JSON so I can manage the 1100+ scenarios included with my module. But it is not a minor undertaking and you need to know at least some Java to reverse engineer the file format.
@bdgza could you share more information about the tool you created to edit VSAV files as text?
You could try pywargame. It is a Python module that allows manipulating Vassal modules (.vmod) and - in a more limited capacity - saves (.vsav) and logs (.vlog). The code can decode and encode the .vsav files. I have used it to generate scenario setups for simple modules - see for example The Battle of Agincourt, with the code at GitLab - in particularly line 2520 in patch.py.
Technically, the code takes a dict defined in line 2021 of patch.py (this could be read from a spreadsheet or the like), the module build file as an XML document, and the module as a Zip archive, and uses pywargame.vassal.SaveFile to make the save, and pywargame.vassal.SaveIO to created the encoded save file.
Yours,
Christian
I wrote my own software in C# .NET. I reverse engineered the file format encoding. It converts the decoded serialised data from the .vsav into JSON as intermediary format so it’s more human readable and easier to work with. I can edit data inside manually or I can write custom updaters to modify .vsavs (in batch) with module updates and changes. It can turn the modified JSON back into an encoded serialised .vsav for VASSAL.
You can use it to generate a .vsav from scratch although I have never done that. I doesn’t know about the Module build file or what components exist in the .vmod. The details are all implemented manually. The .vsav is just represented as its generic VASSAL components.
I’m exploring making some export/import tools on a branch here: GitHub - jamesdterry/vassal: VASSAL, the open-source boardgame engine . They aren’t very tested yet but I’d be up for collaborating.
Perhaps I should say a bit more about pywargame.
You can use the module to read in a .vmod file, and parse the buildFile.xml (or buildFile for older modules) with
from pywargame.vassal import *
with VMod('module.vmod','r') as vmod:
buildFile = BuildFile(vmod.getBuildFile())
When you have the build file, you can get the module definition
game = buildFile.getGame()
Once you have the game, you can query it for all sorts of things, such as maps, global properties, pieces, and so on.
pieces = game.getPieces(asdict=True)
Pieces are fully decoded as a PieceSlot. This has a list of traits you can get an manipulate
traits = piece.getTraits()
You can search for a specific trait to get a representation of it that you can manipulate
deleteTrait = Trait.findTrait(traits,DeleteTrait.ID)
deleteTrait['key'] = key('D')
To update the trait on piece, do
piece.setTraits(*traits)
Most of the classes are simple wrappers around XML DOM elements, so that they are easily exported to XML (again). The one difference are the piece traits which are encoded in a fairly obscure way by VASSAL but are simple structures in pywargame - that’s why the traits are not manipulated directly and are gotten or set by getTraits and setTraits, respectively.
You can also use the module to write a complete module - see for example Battle for Moscow.
For saves, the module reads in the module and then you can specify starting locations for pieces - as done in for example Battle of Agincourt. The module takes care of encoding the pieces state appropriately. Other lines can be added as needed - for example to set Global properties and so on. This is not handled directly by the module, but is fairly easily done.
Yours,
Christian
I did a bit of development using the pywargame API, and I’ve made a script - vsavwrite.py - that generates a save file (.vsav) from a module and a human-readable JSON file. You can find more at GitLab.
An example can be seen here. I have the module BattleForMoscow.vmod (generated by the pywargame API), and the JSON file setup.json
{
"Map": {
"Board": {
"Hexes": {
"0301": ["Soviet 10 Infantry Army" ],
"0302": [ "Soviet 13 Infantry Army" ],
"0303": [ "Soviet 16 Infantry Army" ],
"0304": [ "Soviet 19 Infantry Army" ],
"0405": [ "Soviet 20 Infantry Army" ],
"0504": [ "Soviet 24 Infantry Army" ],
"0505": [ "Soviet 29 Infantry Army" ],
"0506": [ "Soviet 3 Infantry Army" ],
"0507": [ "Soviet 30 Infantry Army" ],
"0508": [ "Soviet 32 Infantry Army" ],
"0509": [ "Soviet 33 Infantry Army" ],
"0510": [ "Soviet 40 Infantry Army" ],
"0803": [ "Soviet 43 Infantry Army" ],
"German LVII Armoured Corps": "0406",
"German LVI Armoured Corps": "0203",
"German XLI Armoured Corps": "0202",
"German XL Armoured Corps": "0305",
"German XLVIII Armoured Corps": "0409",
"German XLVII Armoured Corps": "0408",
"German XLVI Armoured Corps": "0306",
"German XXIV Armoured Corps": "0407",
"German IX Infantry Corps": "0206",
"German LIII Infantry Corps": "0309",
"German VIII Infantry Corps": "0204",
"German VII Infantry Corps": "0106",
"German VI Infantry Corps": "0205",
"German V Infantry Corps": "0103",
"German XIII Infantry Corps": "0310",
"German XII Infantry Corps": "0307",
"German XLII Infantry Corps": "0209",
"German XXIII Infantry Corps": "0104",
"German XX Infantry Corps": "0107",
"German XXVII Infantry Corps": "0201",
"German XXXIV Infantry Corps": "0210",
"German XXXV Infantry Corps": "0410"
},
"German": {},
"Soviet": {
"Soviet": [
"Soviet 49 Infantry Army",
"Soviet 5 Infantry Army",
"Soviet 50 Infantry Army",
"Soviet 1S Infantry Army"
]
},
"Turns": { "1T1": [ "GameTurn" ] }
}
}
}
which I then feed to the script
vsavwrite.py BattleForMoscow.py setup.json -n "Example setup" -o Example.vsav
Note, the script is not omnipotent, and in some cases one is better off developing a custom script using the pywargame API. However, in many cases, the script should get you at least part of the way.
Yours,
Christian
Hi,
Don’t try to alter.vsav as text—you will be destroyed by VASSAL’s encoding. Use pywargame to load the module, modify parts using JSON, and then dump using vsavwrite.py. This is much cleaner and allows you to write configurations rather than searching for hex codes.