z_mirror/fmm
git clone https://git.raiguard.me/z_mirror/fmm
20525de
trunk
1package fmm 2 3import ( 4"archive/zip" 5"compress/zlib" 6"errors" 7"path" 8) 9 10type SaveFileInfo struct { 11MapVersion Version 12BranchVersion uint8 13CampaignName string 14LevelName string 15ScenarioModName string 16Difficulty uint8 17Finished bool 18PlayerWon bool 19NextLevel string 20CanContinue bool 21FinishedButContinuing bool 22SavingReplay bool 23AllowNonAdminDebugOptions bool 24ScenarioVersion Version 25ScenarioBuildVersion uint32 26AllowedCommands AllowedCommands 27LargeBlueprintSize uint32 28ActiveMods []ModIdent 29StartupModSettingsCrc uint32 30StartupModSettings PropertyTree 31} 32 33// Returns information about the given save file. 34func ParseSaveFile (filepath string ) (SaveFileInfo ,error ) { 35info := SaveFileInfo {} 36zipReader ,err := zip .OpenReader (filepath ) 37if err != nil { 38return info ,err 39 } 40defer zipReader .Close () 41 42var dat * zip.File 43compressed := false 44for _ ,file := range zipReader .File { 45name := path .Base (file .Name ) 46if name == "level.dat" || name == "level.dat0" { 47dat = file 48compressed = name == "level.dat0" 49break 50 } 51 } 52if dat == nil { 53return info ,errors .New ("invalid save file: could not locate level data" ) 54 } 55 56rawReader ,err := dat .Open () 57if err != nil { 58return info ,err 59 } 60if compressed { 61rawReader ,err = zlib .NewReader (rawReader ) 62if err != nil { 63return info ,err 64 } 65 } 66defer rawReader .Close () 67 68// TODO: Add tests for various save files. 69r := newDatReader (rawReader ) 70 71info .MapVersion = r .ReadVersionUnoptimized () 72info .BranchVersion = r .ReadUint8 () 73info .CampaignName = r .ReadString () 74info .LevelName = r .ReadString () 75info .ScenarioModName = r .ReadString () 76info .Difficulty = r .ReadUint8 () 77info .Finished = r .ReadBool () 78info .PlayerWon = r .ReadBool () 79info .NextLevel = r .ReadString () 80info .CanContinue = r .ReadBool () 81info .FinishedButContinuing = r .ReadBool () 82info .SavingReplay = r .ReadBool () 83info .AllowNonAdminDebugOptions = r .ReadBool () 84info .ScenarioVersion = r .ReadVersionOptimized (false ) 85 86// Why is this uint32/uint16 instead of uint16/uint8!? 87if info .MapVersion .Cmp (& Version {1 ,2 ,0 ,127 })== VersionGt { 88info .ScenarioBuildVersion = r .ReadUint32 () 89 }else { 90info .ScenarioBuildVersion = uint32 (r .ReadUint16 ()) 91 } 92 93info .AllowedCommands = r .ReadAllowedCommands () 94 95if (info .MapVersion .Cmp (& Version {1 ,2 ,8 ,8 })== VersionGt ) { 96info .LargeBlueprintSize = r .ReadUint32 () 97 } 98 99numMods := r .ReadUint32Optimized () 100info .ActiveMods = make ([]ModIdent ,numMods ) 101for i := uint32 (0 );i < numMods ;i ++ { 102info .ActiveMods [i ]= r .ReadModWithCRC () 103 } 104 105info .StartupModSettingsCrc = r .ReadUint32 () 106 107info .StartupModSettings = r .ReadPropertyTree () 108 109if r .err != nil { 110return info ,errors .Join (errors .New ("failed to parse save file" ),r .err ) 111 } 112 113return info ,nil 114}