Results 1 to 9 of 9

Thread: Documentation on Save Game access?

  1. #1

    Join Date
    29-07-19
    Posts
    6
    vCash
    500

    Documentation on Save Game access?

    I am looking to read data from a Save Game file.

    It looks like it is possible as vfilatov has his scout application, and I remember reading when I was searching how people were modding the game that someone has an open source Delphi repository for getting data (which I stupidly didn't save).

    I've had a search around on the forum but I can't seem to find what I'm looking for so sorry in advance if I am missing something glaringly obvious.

    Is anyone able to point me in the right direction?

  2. #2

    Join Date
    23-06-14
    Posts
    42
    vCash
    500
    I used CMScout sources as info about database structure. You can download the sources here.
    Also you can see my recent similar question here.

  3. The Following User Says Thank You to vfilatov For This Useful Post:


  4. #3

    Join Date
    29-07-19
    Posts
    6
    vCash
    500
    Quote Originally Posted by vfilatov View Post
    I used CMScout sources as info about database structure. You can download the sources here.
    Also you can see my recent similar question here.
    Thank you for the quick response!

    I have also stumbled upon two threads which I think would make my life 10x easier but unfortunately both consist of dead links. Which given the time frame and the absence of JohnLocke for a considerable time they will be lost to the forever.

    The Java Programming Thread
    https://champman0102.co.uk/showthread.php?t=3028

    Reading database/save file
    https://champman0102.co.uk/showthread.php?t=7975

    However I have found a useful post titled John Locke explains... - CM Wiki https://champman0102.co.uk/showthread.php?t=1597 which had a very useful post Patinoz that outlines the Save Game Genome.

    Each save game consists of a series of files, each storing specific data needed by the game.

    This page is to record the structure of a save, the ultimate goal is to identify what every single byte of data stores.

    Files:
    index.dat
    colour.dat
    continent.dat
    contract.dat
    city.dat
    club.dat & nat_club.dat
    first_names.dat, second_names.dat & common_names.dat
    officials.dat
    stadium.dat

  5. #4

    Join Date
    26-09-18
    Location
    Brazil
    Posts
    818
    vCash
    3297
    Mate, just chek the source code of nicks patcher in github https://champman0102.co.uk/showthread.php?t=11661 open it in visual studioit has a working code of savegame read in c#

  6. The Following User Says Thank You to MadScientist For This Useful Post:


  7. #5

    Join Date
    29-07-19
    Posts
    6
    vCash
    500
    Quote Originally Posted by MadScientist View Post
    Mate, just chek the source code of nicks patcher in github https://champman0102.co.uk/showthread.php?t=11661 open it in visual studioit has a working code of savegame read in c#
    I wish you had posted sooner, and I had checked that forum post! CM0102Patcher didn't exactly jump out to me as an exporter and I thought it would just be for hacking the .exe!

    I've literally just spent the last half hour translating the Delphi code from https://sourceforge.net/projects/cme...0Scout%202.00/ to C# which by the looks of it that is what Nick has done! Although I only did the File Name. I might lift and shift his code over to my project, well my intention was to put my specific loading code into a public repo for others to use as there didn't seem to be a readily available option out there until now!

    But none the less Thank you MadScientist!

  8. #6

    Join Date
    23-06-14
    Posts
    42
    vCash
    500
    Unfortunately, I have not found a complete description of the save format anywhere.
    For example, only Flex2 source code (from JohnLocke) contains description of the match_stats_matches.tmp block (where you can find match statistics such a number of passes, key tackles etc.) but still not complete.
    I don't know where current player condition in the save.
    Also nation_comp.dat block format unknown to me. And many other blocks.

  9. #7

    Join Date
    29-07-19
    Posts
    6
    vCash
    500
    Quote Originally Posted by vfilatov View Post
    For example, only Flex2 source code (from JohnLocke) contains description of the match_stats_matches.tmp block (where you can find match statistics such a number of passes, key tackles etc.) but still not complete.
    Do you have a copy of the Fkex2 source code? I believe JLCollection.zip is what I am after but the link is dead. https://champman0102.co.uk/downloads.php?do=file&id=201

    Quote Originally Posted by vfilatov View Post
    I don't know where current player condition in the save.
    I guess that also includes current statuses? So interests, unhappy, injuries etc?

    Quote Originally Posted by vfilatov View Post
    Also nation_comp.dat block format unknown to me. And many other blocks.
    I would have though nation_comp.dat to be similar to that of the club_comp.dat? But I've not looked.

  10. #8

    Join Date
    23-06-14
    Posts
    42
    vCash
    500
    Quote Originally Posted by tonytony View Post
    Do you have a copy of the Fkex2 source code?
    The source code included in the Flex2 tool, I downloaded the tool with source code from the Downloads section of this site.

  11. #9

    Join Date
    18-07-15
    Posts
    252
    vCash
    0
    I'll dump this here instead of it rotting on my hard-drive - here's some code I wrote recently that decompresses and re-compresses CM0102 Save Game files. Can also be used just to dump all the individual files inside the .sav file (as .sav files are like a poor man's .zip really )

    Very rough code below, because once I wrote it, I didn't do anything with it - but you can see how to comment/uncomment it to either compress or decompress:

    Code:
                var file = @"in.sav";
                var fileOut = @"out.sav";
    
                using (var fout = File.Create(fileOut))
                using (var fs = File.OpenRead(file))
                using (var br = new BinaryReader(fs))
                using (var bw = new BinaryWriter(fout))
                {
                    var compressed = (br.ReadInt32() == 4);
    
                    // 3 is uncompressed, 4 is compressed (swap depending on what you're doing)
                    bw.Write((int)4);
    
                    // Skip 4 bytes
                    bw.Write(br.ReadInt32());
    
                    // Read blocks
                    var blockCount = br.ReadInt32();
                    bw.Write(blockCount);
    
                    List<Block> blocks = new List<Block>();
                    for (int i = 0; i < blockCount; i++)
                    {
                        var block = new Block();
                        block.blockBuffer = br.ReadBytes(268);
                        blocks.Add(block);
                    }
    
                    
                    // Compress
                    foreach (var block in blocks)
                    {
                        fs.Seek(block.Position, SeekOrigin.Begin);
                        var ms = new MemoryStream();
                        int lastByte = -1;
                        int lastByteCount = 0;
                        for (var i = 0; i < block.Size; i++)
                        {
                            var b = fs.ReadByte();
    
                            if (lastByte == -1)
                                lastByte = b;
    
                            if (b != lastByte || lastByteCount >= 126)
                            {
                                if (lastByteCount == 1 && lastByte < 128)
                                    ms.WriteByte((byte)lastByte);
                                else
                                {
                                    ms.WriteByte((byte)(lastByteCount + 128));
                                    ms.WriteByte((byte)lastByte);
                                }
    
                                lastByte = b;
                                lastByteCount = 1;
                            }
                            else
                            {
                                lastByteCount++;
                            }
                        }
    
                        // Write last part
                        if (lastByteCount == 1 && lastByte < 128)
                            ms.WriteByte((byte)lastByte);
                        else
                        {
                            ms.WriteByte((byte)(lastByteCount + 128));
                            for (int j = 0; j < lastByteCount; j++)
                                ms.WriteByte((byte)lastByte);
                        }
    
                        block.dataBuffer = ms.ToArray();
                    }
                    
                    /*
                    // Decompress
                    foreach (var block in blocks)
                    {
                        fs.Seek(block.Position, SeekOrigin.Begin);
                        block.dataBuffer = new byte[block.Size];
                        int bufPtr = 0;
                        while (bufPtr < block.Size)
                        {
                            var b = fs.ReadByte();
                            if (b <= 128)
                            {
                                block.dataBuffer[bufPtr++] = (byte)b;
                            }
                            else
                            {
                                byte byteCount = (byte)(b - 128);
                                byte actualByte = (byte)fs.ReadByte();
                                for (byte i = 0; i < byteCount; i++)
                                    block.dataBuffer[bufPtr++] = (byte)actualByte;
                            }
                        }
                    }*/
    
                    fout.Seek((blocks.Count * 268) + 12, SeekOrigin.Begin);
                    
                    // Write Block Data
                    foreach (var block in blocks)
                    {
                        block.Position = (uint)fout.Position;
                        bw.Write(block.dataBuffer);
                    }
    
                    fout.Seek(12, SeekOrigin.Begin);
                    foreach (var block in blocks)
                    {
                        bw.Write(block.blockBuffer);
                    }
                }
    Code:
        public class Block
        {
            public byte[] blockBuffer;
            public byte[] dataBuffer;
            public uint Position
            {
                get
                {
                    return BitConverter.ToUInt32(blockBuffer, 0);
                }
                set
                {
                    Array.Copy(BitConverter.GetBytes(value), 0, blockBuffer, 0, 4);
                }
            }
    
            public uint Size
            {
                get
                {
                    return BitConverter.ToUInt32(blockBuffer, 4);
                }
                set
                {
                    Array.Copy(BitConverter.GetBytes(value), 0, blockBuffer, 4, 4);
                }
            }
    
            public string Name
            {
                get
                {
                    int idx = Array.IndexOf(blockBuffer, (byte)0, 8);
                    return Encoding.ASCII.GetString(blockBuffer, 8, idx - 8);
                }
            }
        }

  12. The Following User Says Thank You to Nick+Co For This Useful Post:


Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •