1

I'm trying to reverse-engineer the file format of a game's save files. The game is written in Unity, thus .NET, so it was possible to view an approximated code with RedGate Reflector.

Right now I know the format is a header, followed by a Deflated stream of map data, which is created with ICSharpZipLib.

The header is composed of two strings, prefixed by a byte with their length. The first string is just a format specifier, the second is a version identifier. After those strings, there is a byte denoting if the contents are compressed or not (but that's hardcoded into the game to always compress).

<byte flen> <byte[flen] format> <byte vlen> <byte[vlen] version> <byte compress> <data>

The game reads the file like this:

  1. open file for reading, create a BinaryReader around the stream.
  2. read format specifier with ReadString
  3. read version with ReadString and int.Parse, warning the player if it's an old save
  4. use ReadBoolean to check if it's a compressed file
    • if true, wraps the file stream in a InflaterInputStream
  5. read the contents of the file into a string, and pass it to other functions

My trouble is reading the contents, I've written a simple C# program that mimics the game's behaviour, using the exact same library version, but I get a "Unexpected EOF". Editing the file to remove the non-compressed header and opening it with other command-line inflate utilities also results in that error. The file is guaranteed valid, since the game can open it without problem.

Below are two example files. The game in question is called Atmosphir, and can be downloaded here.

https://mega.nz/#!f5QFHJ6T!55LvsPFaaAp7Y-ZS3GbLPHg8Ohh-p-5M0AR_Hb7jd1c

https://mega.nz/#!C5xxEapL!ZZ9Tg2kjQetr9KXmfBn5rBc5c5BZ1UUXeqSLJvfJiyQ

  • This is my first question here, please point out any mistakes or close it if it's not a valid question for this site. – Kroltan Jan 25 '16 at 16:55
2

Perhaps your code tries to read more records than specified in the compressed header?

My code below works just fine (with \Atmosphir\Atmosphir_Data\Atmosphir_Data\Managed\ICSharpCode.SharpZipLib.dll as a reference):

using System;
using System.IO;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;

namespace Atmo
{
    class Program
    {
        static void Main(string[] args)
        {
            StreamReader sr = new StreamReader("cc16.atmo");
            BinaryReader br = new BinaryReader(sr.BaseStream);
            Console.WriteLine("Format: " + br.ReadString());
            Console.WriteLine("Version: " + br.ReadString());
            Console.WriteLine("Compressed: " + br.ReadBoolean());
            br = new BinaryReader(new InflaterInputStream(sr.BaseStream));
            int total = br.ReadInt32();
            for (int i = 0; i < total; i++)
            {
                Console.WriteLine(br.ReadString());
            }
        }
    }
}

Output:

Format: AtmoMap
Version: 5
Compressed: True
mi_flag_finish
mi_block_color_white
mi_wood_sticks
mi_block_color_green
mi_block_bricks_1q_cylinder_straight
mi_block_bricks
mi_block_bricks_1q_pipe
mi_ladder_creeper_green
mi_gravity_area_uniform
mi_sandcastle_corner
mi_sandcastle_connection
mi_condition_trigger
mi_skybox_realistic
mi_skybox_adventure
mi_info_sticker
mi_condition_count
mi_skybox_halloween_nightsky
mi_skybox_afternoon
mi_condition_area
mi_bark_slice_platform
mi_waypoint
mi_block_color_blue
mi_block_bricks_white_ledge
mi_ladder_hanging
mi_moving_platform_disc
mi_block_bricks_lightbrown
mi_block_candy_vanilla_cake
mi_candy_floor_orange
mi_sandcastle_tower_top
mi_sandcastle_tower
mi_block_bricks_white
mi_torch
mi_block_bricks_white_2b_diagonal
mi_floor_green
mi_block_monkey
mi_block_bricks_half
mi_bridge_hanging
mi_block_bricks_lightbrown_2b_diagonal
mi_flag_checkpoint
mi_muka_boss
mi_death_skull
mi_muka_shaman
mi_muka_axe_warrior
mi_muka_scout
mi_muka_crypt_keeper
mi_candy_gumdrops_yellow
mi_block_candy_gingerbread_glazed_yellow
mi_candy_gumdrops_green
mi_candy_sugartree_red
mi_candy_sugartree_yellow
mi_candy_floor_violet
mi_candy_sugartree_blue
mi_candy_floor_red
mi_block_candy_gingerbread
mi_block_candy_gingerbread_glazed_pink
mi_candy_gumdrops_red
mi_candy_floor_blue
mi_floor_muka
mi_floor_caved
mi_muka_sticks
mi_block_wooden_bark
mi_torchwall_steel
mi_totem
mi_floor_river_grass
mi_plant
mi_block_crate
mi_flag_start
mi_floor_sand
mi_gate_skull
mi_muka_tent_open
mi_stonehead
mi_muka_openbox
mi_block_bricks_stone
mi_mutation_ammo
mi_fence_straight
mi_2d_force_area
mi_block_monkey_broken
mi_block_bricks_white_floor
mi_block_sand
mi_block_color_black
mi_block_color_yellow
mi_block_color_lightbrown
mi_block_color_lightgreen
mi_block_color_red
mi_block_color_purple
mi_block_candy_gingerbread_sprinkled
mi_block_magma2
mi_block_color_brown
mi_river_water
mi_stones
mi_big_flowers
mi_river_reed
mi_block_sanddirt
mi_block_river_earth
  • How silly of me, not noticing the total i32! But this does not look like it's all the map information, I'll continue investingating. Thanks for the help! – Kroltan Jan 25 '16 at 20:57
  • No problem. Based on your original post, it sounds like you know what you're doing! – Jason Geffner Jan 25 '16 at 21:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.