public void TestReadTestDGF() { IDGF test_dgf = DGFIO.OpenDGF("./test.dgf"); Assert.IsNotNull(test_dgf); Assert.AreEqual("Ethem Kurt", test_dgf.AuthorName); Assert.AreEqual("https://github.com/BigETI/DGFSharp", test_dgf.Comments); Assert.AreEqual((ushort)1, test_dgf.ApplyPlayPasswordUntilGardenNumber); Assert.AreEqual("Edit", test_dgf.EditPassword); Assert.AreEqual("Play", test_dgf.PlayPassword); Assert.AreEqual(3, test_dgf.Garden.Count); foreach (IGarden garden in test_dgf.Garden) { Assert.IsNotNull(garden); } Assert.AreEqual("DAISYG_5.MID", test_dgf.GardenOneMIDIPath); Assert.AreEqual("Round trip around flying ground", test_dgf.Garden[0].Name); Assert.AreEqual("Marmot garden", test_dgf.Garden[1].Name); Assert.AreEqual("Underground garden", test_dgf.Garden[2].Name); Assert.AreEqual(85, test_dgf.Garden[0].Time); Assert.AreEqual(120, test_dgf.Garden[1].Time); Assert.AreEqual(225, test_dgf.Garden[2].Time); Assert.AreEqual("DAISYG_5.MID", test_dgf.Garden[0].MIDIPath); Assert.AreEqual("DAISYG_2.MID", test_dgf.Garden[1].MIDIPath); Assert.AreEqual("DAISYG_6.MID", test_dgf.Garden[2].MIDIPath); Assert.AreEqual(new Size(17, 12), test_dgf.Garden[0].Size); Assert.AreEqual(new Size(17, 12), test_dgf.Garden[1].Size); Assert.AreEqual(new Size(40, 40), test_dgf.Garden[2].Size); Assert.AreEqual(new Position(0, 10), GetDaisysPosition(test_dgf.Garden[0])); Assert.AreEqual(new Position(0, 10), GetDaisysPosition(test_dgf.Garden[1])); Assert.AreEqual(new Position(1, 4), GetDaisysPosition(test_dgf.Garden[2])); }
public void TestOutputDGFFile() { IDGF test_dgf = DGFIO.OpenDGF("./test.dgf"); Assert.IsNotNull(test_dgf); Assert.IsTrue(test_dgf.Save("./output.dgf")); FileAssert.AreEqual("./output.dgf", "./test.dgf"); }
/// <summary> /// Saves Daisy's Garden to the specified file /// </summary> /// <param name="dgf">Daisy's Garden file</param> /// <param name="path">Path</param> /// <returns>"true" if successful, otherwise "false"</returns> public static bool SaveDGF(IDGF dgf, string path) { if (dgf == null) { throw new ArgumentNullException(nameof(dgf)); } if (path == null) { throw new ArgumentNullException(nameof(path)); } bool ret; using (FileStream file_stream = File.OpenWrite(path)) { ret = WriteDGFStream(dgf, file_stream); } return(ret); }
/// <summary> /// Main entry point /// </summary> /// <param name="args">Command line arguments</param> private static void Main(string[] args) { Dictionary <string, string> files = new Dictionary <string, string>(); string output_directory = string.Empty; bool show_help = false; bool verbose = false; EInputFlag current_input_flag = EInputFlag.Nothing; string last_path = null; string help_topic = null; foreach (string arg in args) { string trimmed_arg = arg.Trim().ToLower(); if (trimmed_arg.Length > 0) { if (trimmed_arg[0] == '-') { string key = ((trimmed_arg.Length > 1) ? trimmed_arg.Substring(1) : string.Empty); InputFlag?input_flag = null; if (inputFlags.ContainsKey(key)) { input_flag = inputFlags[key]; } else if (inputFlagAliases.ContainsKey(key)) { input_flag = inputFlags[inputFlagAliases[key]]; } if (input_flag == null) { Console.Error.Write("Invalid input flag \""); Console.Error.Write(arg); Console.Error.WriteLine("\""); } else { PrintInputFlagError(current_input_flag); current_input_flag = input_flag.Value.Flag; switch (current_input_flag) { case EInputFlag.ShowHelp: show_help = true; break; case EInputFlag.Verbose: verbose = true; current_input_flag = EInputFlag.Nothing; break; } } } else { switch (current_input_flag) { case EInputFlag.Nothing: if (!(files.ContainsKey(arg))) { files.Add(arg, arg); } last_path = arg; break; case EInputFlag.ShowHelp: help_topic = trimmed_arg; break; case EInputFlag.SpecifyOutputPath: if (last_path == null) { Console.Error.WriteLine("No input path is defined"); } else { files[last_path] = arg; } current_input_flag = EInputFlag.Nothing; break; case EInputFlag.SpecifyOutputDirectoryPath: output_directory = arg; current_input_flag = EInputFlag.Nothing; break; } current_input_flag = EInputFlag.Nothing; } } } PrintInputFlagError(current_input_flag); if (show_help || (files.Count <= 0)) { if (help_topic == null) { PrintInputFlags(); } else { InputFlag?input_flag = null; if (inputFlags.ContainsKey(help_topic)) { input_flag = inputFlags[help_topic]; } else if (inputFlagAliases.ContainsKey(help_topic)) { input_flag = inputFlags[inputFlagAliases[help_topic]]; } if (input_flag == null) { Console.Error.Write("Invalid help topic \""); Console.Error.Write(help_topic); Console.Error.WriteLine("\"."); PrintInputFlags(); } else { Console.WriteLine(); Console.Write("Help topic: "); Console.WriteLine(input_flag.Value.Description); Console.WriteLine(); string[] full_description = input_flag.Value.FullDescription.Split(Environment.NewLine); if (full_description != null) { foreach (string full_description_line in full_description) { if (full_description_line != null) { Console.Write("\t"); Console.WriteLine(full_description_line); } } } Console.WriteLine(); } } } else { foreach (KeyValuePair <string, string> file in files) { try { string input_file_path = Path.GetFullPath(file.Key); if (File.Exists(input_file_path)) { if (verbose) { Console.Write("Opening file \""); Console.Write(input_file_path); Console.WriteLine("\"..."); } IDGF dgf = DGFIO.OpenDGF(input_file_path); dgf.EditPassword = string.Empty; dgf.PlayPassword = string.Empty; dgf.ApplyPlayPasswordUntilGardenNumber = 1; if (dgf == null) { Console.Error.Write("Could not open file \""); Console.Error.Write(input_file_path); Console.Error.WriteLine("\"."); } else { string output_path = Path.GetFullPath(Path.Combine(output_directory, file.Value)); string directory_path = Path.GetFullPath(Path.GetDirectoryName(output_path)); if (!(Directory.Exists(directory_path))) { Directory.CreateDirectory(directory_path); if (verbose) { Console.Write("Created directory \""); Console.Write(directory_path); Console.WriteLine("\"."); } } if (dgf.Save(output_path)) { if (verbose) { Console.Write("Created file \""); Console.Write(output_path); Console.WriteLine("\"."); } } else { Console.Error.Write("Failed to write output to \""); Console.Error.Write(output_path); Console.Error.WriteLine("\"."); } } } else { Console.Error.Write("File \""); Console.Error.Write(input_file_path); Console.Error.WriteLine("\" does not exist."); } } catch (Exception e) { Console.Error.WriteLine(e); } } } }
/// <summary> /// Writes to Daisy's Garden file stream /// </summary> /// <param name="dgf">Daisy's Garden file</param> /// <param name="stream">Stream</param> /// <returns>"true" if successful, otherwise "false"</returns> public static bool WriteDGFStream(IDGF dgf, Stream stream) { if (dgf == null) { throw new ArgumentNullException(nameof(dgf)); } if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (!stream.CanWrite) { throw new AccessViolationException("Specified DGF stream can not be written to."); } bool ret = false; using (BinaryWriter binary_writer = new BinaryWriter(stream, Encoding.ASCII, true)) { binary_writer.Write(dgfFileHeader); WriteDGFString(binary_writer, gameName); binary_writer.Write(unknownConstantAfterGameName); EncryptAndWriteDGFString(binary_writer, dgf.EditPassword); EncryptAndWriteDGFString(binary_writer, dgf.PlayPassword); EncryptAndWriteDGFString(binary_writer, dgf.ApplyPlayPasswordUntilGardenNumber.ToString()); WriteDGFString(binary_writer, dgf.AuthorName); WriteDGFString(binary_writer, dgf.Comments); WriteDGFString(binary_writer, dgf.GardenOneMIDIPath); binary_writer.Write((ushort)dgf.Garden.Count); binary_writer.Write(unknownConstantAfterGardenCount); binary_writer.Write(Encoding.ASCII.GetBytes(daisysGardenClassName)); binary_writer.Write(outputVersionNumber); bool first = true; foreach (IGarden garden_list_item in dgf.Garden) { if (first) { first = false; } else { binary_writer.Write(dgfFileGardenSeparator); } WriteDGFString(binary_writer, garden_list_item.Name); WriteDGFString(binary_writer, garden_list_item.MIDIPath); ISize garden_size = garden_list_item.Size; binary_writer.Write(garden_size.Width); binary_writer.Write(garden_size.Height); binary_writer.Write(garden_list_item.Time); binary_writer.Write((uint)(garden_size.Width * garden_size.Height)); for (int x, y = 0; y < garden_size.Height; y++) { for (x = 0; x < garden_size.Width; x++) { ETile tile = garden_list_item.GetTile((byte)x, (byte)y); binary_writer.Write((byte)((int)tile & 0xFF)); binary_writer.Write((ushort)(((int)tile >> 8) & 0xFFFF)); } } binary_writer.Write(garden_list_item.Entities.Count); foreach (IEntity entity in garden_list_item.Entities) { binary_writer.Write((byte)((int)entity.Type & 0xFF)); binary_writer.Write((ushort)(((int)entity.Type >> 8) & 0xFFFF)); binary_writer.Write(entity.Position.X); binary_writer.Write(entity.Position.Y); switch (entity.Type) { case EEntityType.Marmot: case EEntityType.RightMovingWorm: case EEntityType.LeftMovingWorm: case EEntityType.UpMovingLift: case EEntityType.DownMovingLift: case EEntityType.LeftMovingLift: case EEntityType.RightMovingLift: binary_writer.Write(entity.Bounds.Left); binary_writer.Write(entity.Bounds.Top); binary_writer.Write(entity.Bounds.Right); binary_writer.Write(entity.Bounds.Bottom); break; case EEntityType.QuestionMark: WriteDGFString(binary_writer, entity.Hint); break; } } } ret = true; } return(ret); }
/// <summary> /// Reads Daisy's Garden file stream /// </summary> /// <param name="stream">Stream</param> /// <returns>DGF if successful, otherwise "null"</returns> public static IDGF ReadDGFStream(Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } IDGF ret = null; if (!stream.CanRead) { throw new AccessViolationException("Specified DGF stream can not be read from."); } using (BinaryReader binary_reader = new BinaryReader(stream, Encoding.ASCII, true)) { if (binary_reader.ReadInt32() != dgfFileHeader) { throw new InvalidDataException("File header does not match the expected file header"); } if (ReadDGFString(binary_reader) != gameName) { throw new InvalidDataException("Game name does not match the expected game name."); } if ((binary_reader.ReadInt16() != 0x7) || (binary_reader.ReadByte() != 0x0)) { throw new InvalidDataException($"Unknown constant after game name does not match the documented unknown constant after game name."); } string edit_password = ReadAndDecryptDGFString(binary_reader); string play_password = ReadAndDecryptDGFString(binary_reader); string apply_play_password_until_garden_number_string = ReadAndDecryptDGFString(binary_reader); if (!ushort.TryParse(apply_play_password_until_garden_number_string, out ushort apply_play_password_until_garden_number)) { throw new InvalidDataException($"Failed to parse \"apply play password until garden number\"."); } string author_name = ReadDGFString(binary_reader); string comments = ReadDGFString(binary_reader); string garden_one_midi_path = ReadDGFString(binary_reader); ushort number_of_garden = binary_reader.ReadUInt16(); byte constant = binary_reader.ReadByte(); bool failure = true; if (constant == 0x0) { failure = false; } else if (constant == 0xFF) { if (binary_reader.ReadInt32() == 0x090001FF) { failure = binary_reader.ReadByte() != 0x0; } } if (failure) { throw new InvalidDataException($"Unknown constant after the number of garden does not match to any of the documented values."); } if (Encoding.ASCII.GetString(binary_reader.ReadBytes(daisysGardenClassName.Length)) != daisysGardenClassName) { throw new InvalidDataException($"Incorrect Daisy's Garden class name."); } ushort program_version = binary_reader.ReadUInt16(); if ((program_version != 0x1) && (program_version != 0x2)) { throw new InvalidDataException($"Program version \"{ program_version }\" is not supported."); } List <IGarden> garden = new List <IGarden>(); for (ushort garden_index = 0; garden_index != number_of_garden; garden_index++) { if ((garden_index > 0) && (binary_reader.ReadInt32() != dgfFileGardenSeparator)) { throw new InvalidDataException($"Incorrect garden seperator."); } string garden_name = ReadDGFString(binary_reader); string garden_midi_path = ReadDGFString(binary_reader); ushort garden_width = binary_reader.ReadUInt16(); ushort garden_height = binary_reader.ReadUInt16(); ushort garden_time = binary_reader.ReadUInt16(); uint garden_tile_count = binary_reader.ReadUInt32(); if (garden_tile_count != (garden_width * garden_height)) { throw new InvalidDataException($"Garden tile count does not match with garden width times garden height."); } ETile[,] tile_grid = new ETile[garden_width, garden_height]; for (uint cell_index = 0U; cell_index != garden_tile_count; cell_index++) { byte tile_id = binary_reader.ReadByte(); ushort tile_variant = binary_reader.ReadUInt16(); ETile computed_tile = (ETile)(tile_id | (tile_variant << 8)); if (tileEnumItems.Contains(computed_tile)) { tile_grid[cell_index % garden_width, cell_index / garden_width] = computed_tile; } } uint number_of_entities = binary_reader.ReadUInt32(); List <IEntity> entities = new List <IEntity>(); for (uint entity_index = 0; entity_index < number_of_entities; entity_index++) { byte entity_type = binary_reader.ReadByte(); ushort entity_variant = binary_reader.ReadUInt16(); EEntityType computed_entity = (EEntityType)(entity_type | (entity_variant << 8)); if (entityEnumItems.Contains(computed_entity)) { IPosition position = new Position(binary_reader.ReadUInt16(), binary_reader.ReadUInt16()); IBounds bounds = Bounds.Infinite; string hint = string.Empty; switch (computed_entity) { case EEntityType.Daisy: case EEntityType.RedKey: case EEntityType.YellowKey: case EEntityType.GreenKey: case EEntityType.Apple: case EEntityType.Lemon: case EEntityType.Cherry: case EEntityType.Pineapple: case EEntityType.Garlic: case EEntityType.Mushroom: case EEntityType.Spinach: case EEntityType.Carrot: case EEntityType.Sunflower: case EEntityType.Tulip: case EEntityType.YellowDaisy: case EEntityType.Rose: break; case EEntityType.Marmot: case EEntityType.RightMovingWorm: case EEntityType.LeftMovingWorm: case EEntityType.UpMovingLift: case EEntityType.DownMovingLift: case EEntityType.LeftMovingLift: case EEntityType.RightMovingLift: short left = binary_reader.ReadInt16(); short top = binary_reader.ReadInt16(); short right = binary_reader.ReadInt16(); short bottom = binary_reader.ReadInt16(); bounds = new Bounds(top, bottom, left, right); break; case EEntityType.QuestionMark: hint = ReadDGFString(binary_reader); break; default: throw new InvalidDataException($"Invalid entity type \"{ (int)computed_entity }\""); } entities.Add(new Entity(computed_entity, position, bounds, hint)); } } garden.Add(new Garden(garden_name, garden_midi_path, garden_time, tile_grid, entities)); } ret = new DGF(edit_password, play_password, apply_play_password_until_garden_number, author_name, comments, garden_one_midi_path, garden); } return(ret); }