private Screen LoadScreen(Story_Crafter.MemoryStream data) { if (data.Position == data.Length) { return(null); } Screen s = new Screen(); try { // Read x coordinate. // I allow any number of characters at the beginning of the file due to a strange bug in the stock editor that results in several megabytes of garbage before the screen data begins in a some levels. char c; do { c = (char)data.ReadByte(); } while(c != 'x'); if (c != 'x') { throw new FileCorruptException("Could not find an x coordinate."); } string read = ""; c = (char)data.ReadByte(); while (c != 'y') { if (!char.IsDigit(c)) { throw new FileCorruptException("Non-numeric characters present in x coordinate."); } read += c; c = (char)data.ReadByte(); } s.X = int.Parse(read); // Read y coordinate. Almost exactly as above, but not really worth an entire function. read = ""; c = (char)data.ReadByte(); while (c != '\0') { if (!char.IsDigit(c)) { throw new FileCorruptException("Non-numeric characters present in y coordinate."); } read += c; c = (char)data.ReadByte(); } s.Y = int.Parse(read); // Ensure there is enough data left in the stream (4 + 12 * 250 + 6 = 3010) before I go crazy allocating memory. // I didn't do this before because the length of the coordinates is variable. if (data.Length - data.Position < 3010) { throw new FileCorruptException("Not enough data is present."); } // Check the constant for integrity. It starts at 1 because the first byte is checked finding the y coordinate. for (int i = 1; i < 5; i++) { if (data.ReadByte() != Program.Signature[i]) { throw new FileCorruptException("Signature is missing."); } } // Now begin reading actual tile data. for (int i = 0; i < 4; i++) // Tile layers. { s.Layers[i] = new TileLayer(i, data.Read(250)); } for (int i = 4; i < 8; i++) // Object layers. { s.Layers[i] = new ObjectLayer(i, data.Read(500)); } // Read which assets the screen uses. s.TilesetA = data.ReadByte(); s.TilesetB = data.ReadByte(); s.AmbianceA = data.ReadByte(); s.AmbianceB = data.ReadByte(); s.Music = data.ReadByte(); s.Gradient = data.ReadByte(); } catch (IOException) { throw new FileCorruptException("I/O exception."); } return(s); }
public void Save(string worldIniText = "") { // Write screen data to memory. Story_Crafter.MemoryStream data = new Story_Crafter.MemoryStream(); BinaryWriter writer = new BinaryWriter(data); foreach (Screen s in Screens) { writer.Write(("x" + s.X + "y" + s.Y).ToCharArray()); writer.Write(Program.Signature); for (int i = 0; i < 4; i++) { foreach (Tile t in s.Layers[i].Tiles) { writer.Write((byte)((t.Tileset == 1 ? 128 : 0) + t.Index)); } } for (int i = 4; i <= 7; i++) { byte[] banks = new byte[250]; int j = 0; foreach (Tile t in s.Layers[i].Tiles) { writer.Write((byte)t.Index); banks[j] = (byte)t.Bank; j++; } writer.Write(banks); } writer.Write((byte)s.TilesetA); writer.Write((byte)s.TilesetB); writer.Write((byte)s.AmbianceA); writer.Write((byte)s.AmbianceB); writer.Write((byte)s.Music); writer.Write((byte)s.Gradient); } // Compress and save screen data. FileStream mapBin = new FileStream(this.Path + @"\Map.bin", FileMode.Create, FileAccess.Write); GZipStream zipStream = new GZipStream(mapBin, CompressionMode.Compress); zipStream.Write(data.GetBuffer(), 0, (int)data.Length); zipStream.Close(); mapBin.Close(); data.Close(); writer.Close(); // Update World.ini. File.WriteAllText(WorldIni.Path, worldIniText); WorldIni.Write("World", "Author", this.Author); WorldIni.Write("World", "Name", this.Title); WorldIni.Write("World", "Description", this.Description); WorldIni.Write("World", "Size", this.Size); WorldIni.Write("World", "Category A", this.CategoryA); WorldIni.Write("World", "Category B", this.CategoryB); WorldIni.Write("World", "Difficulty A", this.DifficultyA); WorldIni.Write("World", "Difficulty B", this.DifficultyB); WorldIni.Write("World", "Difficulty C", this.DifficultyC); WorldIni.Write("World", "Clothes", (this.Clothes.R + this.Clothes.G * 256 + this.Clothes.B * 65536).ToString()); WorldIni.Write("World", "Skin", (this.Skin.R + this.Skin.G * 256 + this.Skin.B * 65536).ToString()); // Update DefaultSavegame.ini. IniFile saveINI = new IniFile(this.Path + @"\DefaultSavegame.ini"); saveINI.Write("Positions", "X Map", this.DefaultSave.MapX.ToString()); saveINI.Write("Positions", "Y Map", this.DefaultSave.MapY.ToString()); saveINI.Write("Positions", "X Pos", this.DefaultSave.ScreenX.ToString()); saveINI.Write("Positions", "Y Pos", this.DefaultSave.ScreenY.ToString()); for (int i = 0; i < 12; i++) { saveINI.Write("Powers", "Power" + i, this.DefaultSave.Powers[i].ToString()); } // Update path. string newPath = Program.WorldsPath + "\\" + this.Author + " - " + this.Title; if (newPath != this.Path) { Directory.Move(this.Path, newPath); this.Path = newPath; } // Save patterns try { using (FileStream patternsFile = new FileStream(this.Path + @"\.story_crafter_patterns", FileMode.Create, FileAccess.Write)) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(patternsFile, Patterns); } } catch { // TODO error message } }
public Story(string path) { this.Path = path; // Extract World.ini info. this.WorldIni = new IniFile(this.Path + @"\World.ini"); this.Author = WorldIni.Read("World", "Author"); this.Title = WorldIni.Read("World", "Name"); this.Description = WorldIni.Read("World", "Description"); this.Size = WorldIni.Read("World", "Size"); this.CategoryA = WorldIni.Read("World", "Category A"); this.CategoryB = WorldIni.Read("World", "Category B"); this.DifficultyA = WorldIni.Read("World", "Difficulty A"); this.DifficultyB = WorldIni.Read("World", "Difficulty B"); this.DifficultyC = WorldIni.Read("World", "Difficulty C"); try { int clothes = int.Parse(this.WorldIni.Read("World", "Clothes")); this.Clothes = Color.FromArgb(clothes & 0xFF, (clothes & 0xFF00) / 256, clothes / 65536); // Bit operations... best just move along. } catch (Exception) { this.Clothes = Program.Clothes; } try { int skin = int.Parse(this.WorldIni.Read("World", "Skin")); this.Skin = Color.FromArgb(skin & 0xFF, (skin & 0xFF00) / 256, skin / 65536); } catch (Exception) { this.Skin = Program.Skin; } // Extract DefaultSavegame.ini info. IniFile saveIni = new IniFile(this.Path + @"\DefaultSavegame.ini"); this.DefaultSave = new SaveInfo(); this.DefaultSave.MapX = Program.ParseInt(saveIni.Read("Positions", "X Map"), 1000); this.DefaultSave.MapY = Program.ParseInt(saveIni.Read("Positions", "Y Map"), 1000); this.DefaultSave.ScreenX = Program.ParseInt(saveIni.Read("Positions", "X Pos"), 0); this.DefaultSave.ScreenY = Program.ParseInt(saveIni.Read("Positions", "Y Pos"), 0); this.DefaultSave.Powers = new bool[12]; for (int i = 0; i < 12; i++) { this.DefaultSave.Powers[i] = saveIni.Read("Powers", "Power" + i) == "1"; } // Extract data from Map.bin. FileStream fileIn = File.OpenRead(this.Path + @"\Map.bin"); Story_Crafter.MemoryStream data = new Story_Crafter.MemoryStream(); GZipStream zipStream = new GZipStream(fileIn, CompressionMode.Decompress); zipStream.CopyTo(data); zipStream.Close(); fileIn.Close(); data.Seek(0, SeekOrigin.Begin); // Load all the screens. 1000 screens currently take about 25 mb of memory not including thumbnails. this.Screens = new List <Screen>(); try { Screen s = this.LoadScreen(data); while (s != null) { this.Screens.Add(s); s = this.LoadScreen(data); } } catch (Exception) { throw; } finally { GC.Collect(); data.Close(); } if (Screens.Count == 0) // If there are no screens, create a blank one to start on. { Screen start = new Screen(1000, 1000); this.Screens.Add(start); this.ActiveScreen = start; } else { this.ActiveScreen = GetScreen(this.DefaultSave.MapX, this.DefaultSave.MapY); // Try the screen given as Juni's starting position. if (this.ActiveScreen == null) { this.ActiveScreen = this.GetScreen(1000, 1000); // If that screen doesn't exist, try x1000y1000. if (this.ActiveScreen == null) { this.ActiveScreen = this.Screens[0]; // If that doesn't exist either, use whatever the first screen in Map.bin is. } } } // Load patterns this.Patterns = new List <Pattern>(); try { using (FileStream patternsFile = new FileStream(this.Path + @"\.story_crafter_patterns", FileMode.Open, FileAccess.Read)) { BinaryFormatter formatter = new BinaryFormatter(); Patterns = (List <Pattern>)formatter.Deserialize(patternsFile); } } catch { // TODO error message } }