public void Save(string filepath) { Random rnd = new Random(); //create a ctar1 archive octar1Archive arch = new octar1Archive(); ////write in the file rootf\\version.txt the mod save version. we are in version 1 so it's "v1" in lower case. //create the file ctar1File ctfVersionFile = arch.CreateFile("rootf\\version.txt"); //create the memory stream MemoryStream wwwms = new MemoryStream(); MemVoid.WriteStr(wwwms, "v1"); //get the byte[] data ctfVersionFile.Content = wwwms.GetBuffer(); //dispose it wwwms.Dispose(); ////write int the file rootf\\modinfo informatinos about the mod like the mod name //create the file ctar1File ctfModFile = arch.CreateFile("rootf\\modinfo"); //create the memory stream wwwms = new MemoryStream(); ////wrte every content inside /* the save format for this file is the same as for every items. you can read the big comment for the items under this * * every block ends with a null * * 0x01 beginning of the mod name. utf8 * * * 0xff end of the file * */ //write the mod name wwwms.WriteByte(1); MemVoid.WriteStrUTF8(wwwms, this.ModName); wwwms.WriteByte(0); //end of the mod name //end of the file wwwms.WriteByte(255); //we've finished generating the file that contains the mod's properties ctfModFile.Content = wwwms.GetBuffer(); wwwms.Dispose(); ////create the folders for saving items arch.CreateFolder("rootf\\Items"); arch.CreateFolder("rootf\\Items\\Images"); ////save the items int index = 0; //used for the save file name of the items foreach (ModItem mi in this.listItems) { //the png format is used because it keeps transparency string FileImgName = this.RemoveAccents(mi.ItemName) + "_" + this.GetRandomName(rnd, 10) + ".png"; ////save the image of the item ctar1File ctfImg = arch.CreateFile("rootf\\Items\\Images\\" + FileImgName); //ctar1 files content are byte[]. //here we save the item's image inside a memory stream and we get the byte[] data from that memory stream. MemoryStream ms = new MemoryStream(); mi.Img.Save(ms, System.Drawing.Imaging.ImageFormat.Png); ctfImg.Content = ms.GetBuffer(); ms.Dispose(); //we just need it to get the bytes. //items must be saved in the same order as they are inside the list this.listItems and must be restored in the same order as they are inside the save, inside that virtual folder. //for this reason, every item file name begins with "item[6 digit number index]_". //it's not that ctar1 object load the file is alphabetical order, but it's just to be sure. ////save the item itself string FileItemName = "item" + index.ToString().PadLeft(6, "0".ToCharArray()[0]) + "_" + this.RemoveAccents(mi.ItemName) + "_" + this.GetRandomName(rnd, 10) + ".item"; ctar1File ctfItem = arch.CreateFile("rootf\\Items\\" + FileItemName); //create the memory stream (used to generate the byte[]) and write everything inside. ms = new MemoryStream(); /* * in this file/memory stream, we will save multiple thing. * of a ModItem, we have to save the properties ItemName, ModName, IsBelt and the file name of the image previously saved above this comment. * * most of the properties we have to save are strings. and the bool IsBelt won't be a problem to save. * * * how it's saved and how the file must be read in the futur : * * there's always the first byte that tells what property is comming. for string, you read next bytes until you encounter a null caracter 0x00. for the bool, * you read the next byte and the byte after that one will be a null caracter 0x00. * * * string : [byte that describe what property is comming] then [multiple bytes who is the content/the string/the property value in utf8] then [0x00] ... next thing * bool : [byte that describe what property is comming] then [a byte 0x00 or a byte 0x01] then [0x00] ... next thing * * for the string, we use System.Text.Encoding.UTF8 to encode and decode the strings to and from byte[]. so there's no problem for accents éèêë and every letters. * for the bool, if the next byte is 0x00, it's false. if the next byte is 0x01, it's true. * * * bytes who describe what property is comming: * -0x01 item name string * -0x02 mod name string * -0x03 is belt bool * -0x04 image file name string * * -0xff end of the file, we must stop reading, nothing else is comming after. * */ //MemVoid is located inside octar1ArchiveSaver.cs at the top of the file. ////write the item name ms.WriteByte(1); //beginning of the item name MemVoid.WriteStrUTF8(ms, mi.ItemName); //the item name ms.WriteByte(0); //end of the item name ////write the mod name ms.WriteByte(2); //beginning of the mod name MemVoid.WriteStrUTF8(ms, mi.ItemModName); //the mod name ms.WriteByte(0); //end of the mod name ////write the file name of the associated image ms.WriteByte(4); //beginning of the image file name MemVoid.WriteStrUTF8(ms, FileImgName); //the image file name ms.WriteByte(0); //end of the image file name ////write IsBelt. it's a bool property ms.WriteByte(3); //beginning of the IsBelt //write the bool value if (mi.IsBelt) { //true ms.WriteByte(1); } else { //false ms.WriteByte(0); } ms.WriteByte(0); //end of the IsBelt ms.WriteByte(255); //0xff end of the file //get the byte[] data and finish ctfItem.Content = ms.GetBuffer(); ms.Dispose(); //next iteration index++; } ////create the folders for saving crafts arch.CreateFolder("rootf\\Crafts"); ////save the crafts index = 0; //used for the save file name of the crafts foreach (ModCraft mc in this.listCrafts) { //create a file for the craft string SaveFileName = "craft_" + index.ToString().PadLeft(6, "0".ToCharArray()[0]) + ".craft"; //create the file ctar1File ctfCraft = arch.CreateFile("rootf\\Crafts\\" + SaveFileName); //create the memory stream MemoryStream ms = new MemoryStream(); //the save system and read system for crafts are identical to the save and read system for items. /* * crafts has multiple things to save : recipe, mutiple inputs, multiple output, bool IsMadeInFurnace. * * recipe, inputs, outputs are all a combinaisons of 2 strings : item name and item's mod name. instead of saving a list structure, we treat every refItem the same way. when reading a file, * when encountering the beginning of a 2 string pair (recipe or input or output), it will be followed by a string, a null, a string, and finally a null. * * there are 4 type of "block": a recipe, a input, a output, the IsMadeInFurnace. * * they are encoded this way: * * recipe : [byte that describe what property is comming] then [string utf8 which is the item name] then [0x00] then [string utf8 which is the item's mod name] then [0x00] ... next thing * input : [byte that describe what property is comming] then [string utf8 which is the item name] then [0x00] then [string utf8 which is the item's mod name] then [0x00] ... next thing * output : [byte that describe what property is comming] then [string utf8 which is the item name] then [0x00] then [string utf8 which is the item's mod name] then [0x00] ... next thing * is made in furnace : [byte that describe what property is comming] then [a byte 0x00 or a byte 0x01] then [0x00] ... next thing * * * "byte that describe what property is comming" means the one byte that begins every block. * * the beginning byte for every type of "block" : * -0x01 recipe string string * -0x02 input string string * -0x03 output string string * -0x04 is made in furnace bool * * */ //save the recipe ms.WriteByte(1); //the beginning MemVoid.WriteStrUTF8(ms, mc.Recipe.ItemName); ms.WriteByte(0); //end of the item name MemVoid.WriteStrUTF8(ms, mc.Recipe.ModName); ms.WriteByte(0); //end of the mod name and end of the recipe //save the inputs foreach (refItem ri in mc.Inputs) { ms.WriteByte(2); //beginning of an input MemVoid.WriteStrUTF8(ms, ri.ItemName); ms.WriteByte(0); //end of the item name MemVoid.WriteStrUTF8(ms, ri.ModName); ms.WriteByte(0); //end of the mod name and end of the input } //save the outputs foreach (refItem ri in mc.Outputs) { ms.WriteByte(3); //beginning of an output MemVoid.WriteStrUTF8(ms, ri.ItemName); ms.WriteByte(0); //end of the item name MemVoid.WriteStrUTF8(ms, ri.ModName); ms.WriteByte(0); //end of the mod name and end of the output } //save is made in furnace ms.WriteByte(4); //beginning of is made in furnace if (mc.IsMadeInFurnace) { //true ms.WriteByte(1); } else { //false ms.WriteByte(0); } ms.WriteByte(0); //end of is made in furnace //end of the file ms.WriteByte(255); //put the content inside the file object ctfCraft.Content = ms.GetBuffer(); ms.Dispose(); //we don't need it anymore //next iteration index++; } octar1ArchiveSaver.SaveArchive(arch, filepath); }
public oMod(string filepath) { ////load the archive octar1Archive arch = octar1ArchiveSaver.LoadArchive(filepath); ////load the mod properties written in rootf\\modinfo ctar1File ctfModInfo = arch.GetFileFromPath("rootf\\modinfo"); //create the memory stream to read the file MemoryStream wwwms = new MemoryStream(ctfModInfo.Content); //read the content of the file while (true) { byte IndicationByte = (byte)(wwwms.ReadByte()); //if end of the file if (IndicationByte == 255) { break; } //0x01 is mod name if (IndicationByte == 1) { byte[] byteModName = MemVoid.ReadByteUntilByte(wwwms, 0); this.ModName = System.Text.Encoding.UTF8.GetString(byteModName); } } //we have finished reading the mod info file wwwms.Dispose(); ////load the items ctar1Folder FolderItem = arch.GetFolderFromPath("rootf\\Items"); //get the folder of the items foreach (ctar1File f in FolderItem.listSubFile) { //we create the variable to get the properties values. string strItemName = ""; string strItemModName = ""; Bitmap img = null; bool boolIsBelt = true; //create the memory stream used to read the content of the file MemoryStream ms = new MemoryStream(f.Content); //read the content while (true) { /* * * * bytes who describe what property is comming: * -0x01 item name string * -0x02 mod name string * -0x03 is belt bool * -0x04 image file name string * * -0xff end of the file, we must stop reading, nothing else is comming after. * */ //the first byte of each block indicate what's comming next. byte IndicationByte = (byte)(ms.ReadByte()); //255 is the end of the file if (IndicationByte == 255) { break; } //item name if (IndicationByte == 1) { //get the data as byte[] and then convert it to a string byte[] byteStr = MemVoid.ReadByteUntilByte(ms, 0); strItemName = System.Text.Encoding.UTF8.GetString(byteStr); //Program.wdebug(strItemName + " : " + strItemName.Length.ToString()); } //mod name if (IndicationByte == 2) { //get the data as byte[] and then convert it to a string byte[] byteStr = MemVoid.ReadByteUntilByte(ms, 0); strItemModName = System.Text.Encoding.UTF8.GetString(byteStr); } //IsBelt if (IndicationByte == 3) { byte byteValue = (byte)(ms.ReadByte()); boolIsBelt = byteValue == 1; //read the null byte that end the bool value ms.ReadByte(); } //image file name if (IndicationByte == 4) { //get the data as byte[] and then convert it to a string byte[] byteStr = MemVoid.ReadByteUntilByte(ms, 0); string strImageFileName = System.Text.Encoding.UTF8.GetString(byteStr); //get the file of the image ctar1File FileImage = arch.GetFileFromPath("rootf\\Items\\Images\\" + strImageFileName); //creates a memory stream for the constructor MemoryStream msimage = new MemoryStream(FileImage.Content); img = new Bitmap(msimage); msimage.Dispose(); } } //we have finished to read the content ms.Dispose(); //create the ModItem ModItem newmi = new ModItem(strItemName, strItemModName, img); newmi.IsBelt = boolIsBelt; this.listItems.Add(newmi); } ////load the crafts ctar1Folder FolderCraft = arch.GetFolderFromPath("rootf\\Crafts"); //get the folder of the crafts foreach (ctar1File f in FolderCraft.listSubFile) { //these variables will be defined during the reading process refItem riRecipe = new refItem("", ""); List <refItem> listInputs = new List <refItem>(); //inputs and outputs will be added during the reading process List <refItem> listOutputs = new List <refItem>(); bool ismadeinfurnace = false; //create the memory stream used to read the content MemoryStream ms = new MemoryStream(f.Content); //read the file while (true) { /* * * the beginning byte for every type of "block" : * -0x01 recipe string string * -0x02 input string string * -0x03 output string string * -0x04 is made in furnace bool * */ byte IndicationByte = (byte)(ms.ReadByte()); //if end of the file if (IndicationByte == 255) { break; } //if recipe if (IndicationByte == 1) { //read the recipe name byte[] byteItemName = MemVoid.ReadByteUntilByte(ms, 0); //read until null caracter 0 riRecipe.ItemName = System.Text.Encoding.UTF8.GetString(byteItemName); //read the recipe mod name byte[] byteModName = MemVoid.ReadByteUntilByte(ms, 0); riRecipe.ModName = System.Text.Encoding.UTF8.GetString(byteModName); } //if an input if (IndicationByte == 2) { byte[] byteItemName = MemVoid.ReadByteUntilByte(ms, 0); byte[] byteModName = MemVoid.ReadByteUntilByte(ms, 0); refItem newinput = new refItem(); newinput.ItemName = System.Text.Encoding.UTF8.GetString(byteItemName); newinput.ModName = System.Text.Encoding.UTF8.GetString(byteModName); //add the new input to the list listInputs.Add(newinput); } //if an output if (IndicationByte == 3) { byte[] byteItemName = MemVoid.ReadByteUntilByte(ms, 0); byte[] byteModName = MemVoid.ReadByteUntilByte(ms, 0); refItem newoutput = new refItem(); newoutput.ItemName = System.Text.Encoding.UTF8.GetString(byteItemName); newoutput.ModName = System.Text.Encoding.UTF8.GetString(byteModName); //add the new output to the list listOutputs.Add(newoutput); } //if is made in furnace if (IndicationByte == 4) { byte byteValue = (byte)(ms.ReadByte()); ismadeinfurnace = byteValue == 1; //read the null byte that end the bool ms.ReadByte(); } } //we have finished to read the content ms.Dispose(); //create the ModCraft ModCraft newcraft = new ModCraft(riRecipe, listInputs.ToArray(), listOutputs.ToArray(), ismadeinfurnace); this.listCrafts.Add(newcraft); } }