diff --git a/FileHelper.cs b/FileHelper.cs index 8e6325d..2e4c6f6 100644 --- a/FileHelper.cs +++ b/FileHelper.cs @@ -24,6 +24,20 @@ namespace MHFOldShopTools } } + public static bool LoadFile(string FilePath, out string[] lines) + { + try + { + lines = File.ReadAllLines(FilePath); + return true; + } + catch (Exception ex) + { + lines = null; + return false; + } + } + public static string[] GetDirFile(string Path) { return Directory.GetFiles(Path); @@ -33,6 +47,7 @@ namespace MHFOldShopTools { return Encoding.Default.GetBytes(value); } + public static bool SaveFile(string FilePath, byte[] buffer) { try @@ -52,5 +67,11 @@ namespace MHFOldShopTools return false; } } + + + public static void SaveFile(string FilePath, string[] strArr) + { + System.IO.File.WriteAllLines(FilePath, strArr); + } } } diff --git a/Program.cs b/Program.cs index 8f9c0f0..32a2861 100644 --- a/Program.cs +++ b/Program.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Collections.Generic; +using System.Text; namespace MHFOldShopTools { @@ -6,8 +7,7 @@ namespace MHFOldShopTools { static string loc = Path.GetDirectoryName(AppContext.BaseDirectory) + "\\"; - const string InDir = "Input"; - const string OutDir = "Out"; + const string InDir = "Files"; const string Ver = "0.1"; static void Main(string[] args) @@ -19,52 +19,97 @@ namespace MHFOldShopTools if (!Directory.Exists(loc + InDir)) { - Console.WriteLine("Input文件不存在"); + Console.WriteLine("Files文件不存在"); Console.ReadLine(); return; } - //if (!Directory.Exists(loc + OutDir)) - //{ - // Console.WriteLine("Out文件不存在"); - // Console.ReadLine(); - // return; - //} - - //if (!Directory.Exists(loc + PosFile2DosDir)) - //{ - // Console.WriteLine("Templete文件不存在"); - // Console.ReadLine(); - // return; - //} - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + int bflag = 0; + string[] files = FileHelper.GetDirFile(loc + InDir); - Console.WriteLine($"共{files.Length}个文件,是否处理? (y/n)"); - - string yn = Console.ReadLine(); - if (yn.ToLower() != "y") - return; - - int index = 0; - int errcount = 0; - for (int i = 0; i < files.Length; i++) + while (true) { - string FileName = files[i].Substring(files[i].LastIndexOf("\\")); + Console.WriteLine($"请确保Files目录中已放置解密的MHF-FW5 mhfdat.bin文件,请选择:"); + Console.WriteLine($"[1],解析Files目录中bin文件,生成同名txt清单和csv表"); + Console.WriteLine($"[2],解析Files目录中csv文件,修改回同名的bin文件中"); + Console.WriteLine($"Please ensure that the decrypted MHF-FW5 mhfdat.bin file is placed in the Files directory. Please select:"); + Console.WriteLine($"[1],Parse the .bin file in the Files directory and generate a .txt list and .csv table with the same name"); + Console.WriteLine($"[2],Parse the .csv file in the Files directory and modify it back to the .bin file with the same name"); - if (!FileName.ToLower().Contains(".bin")) + string yn = Console.ReadLine(); + if (yn.ToLower() == "1") + bflag = 1; + else if(yn.ToLower() == "2") + bflag = 2; + + if (bflag != 0) + break; + } + + if (bflag == 1) + { + int index = 0; + int errcount = 0; + for (int i = 0; i < files.Length; i++) { - continue; + string FileName = files[i].Substring(files[i].LastIndexOf("\\")); + + if (System.IO.Path.GetExtension(FileName).ToLower() != ".bin") + { + continue; + } + index++; + + Console.WriteLine($">>>>>>>>>>>>>>开始处理 第{index}个文件 {FileName}<<<<<<<<<<<<<<<<<<<"); + FileHelper.LoadFile(files[i], out byte[] data); + + ReaderItems(data, out List OutInputString, out List outPutCsv); + + + string listfileName = System.IO.Path.GetFileNameWithoutExtension(FileName) + ".txt"; + string listoutpath = loc + InDir + "\\" + listfileName; + FileHelper.SaveFile(listoutpath, OutInputString.ToArray()); + + string csvfileName = System.IO.Path.GetFileNameWithoutExtension(FileName) + ".csv"; + string csvoutpath = loc + InDir + "\\" + csvfileName; + FileHelper.SaveFile(csvoutpath, outPutCsv.ToArray()); + + Console.WriteLine($">>>>>>>>>>>>>>处理完毕>>>>>>>>>>>>>>"); } - index++; + } + else + { + int index = 0; + int errcount = 0; + for (int i = 0; i < files.Length; i++) + { + string FileName = files[i].Substring(files[i].LastIndexOf("\\")); - Console.WriteLine($">>>>>>>>>>>>>>开始处理 第{index}个文件 {FileName}<<<<<<<<<<<<<<<<<<<"); - FileHelper.LoadFile(files[i], out byte[] data); + if (System.IO.Path.GetExtension(FileName).ToLower() != ".csv") + { + continue; + } + index++; - ReaderItems(data); - Console.WriteLine($">>>>>>>>>>>>>>处理完毕"); + Console.WriteLine($">>>>>>>>>>>>>>开始处理 第{index}个文件 {FileName}<<<<<<<<<<<<<<<<<<<"); + FileHelper.LoadFile(files[i], out string[] lines); + List itemlist = LoadStructForCsv(lines); + + + string binfileName = System.IO.Path.GetFileNameWithoutExtension(FileName) + ".bin"; + string binoutpath = loc + InDir + "\\" + binfileName; + + FileHelper.LoadFile(binoutpath, out byte[] bindata); + + ModifyItem(bindata, itemlist, out byte[] ResultData); + + FileHelper.SaveFile(binoutpath, ResultData); + + Console.WriteLine($">>>>>>>>>>>>>>处理完毕>>>>>>>>>>>>>>"); + } } while (true) @@ -74,16 +119,40 @@ namespace MHFOldShopTools } - static int StartPtr = 0x539A78; + static int NPStore_Ptr = 0x537924; const int _singelItemDatalenght = 12; - static void ReaderItems(byte[] data) - { - List items = new List(); + static int NPStore_ItemCount = 712; + static void ReaderItems(byte[] data,out List OutInput,out List outPutCsv) + { + OutInput = new List(); + outPutCsv = new List(); + List items = new List(); + for (int i = 0; i < NPStore_ItemCount; i++) + { + items.Add(GetShopItemInfo(data, NPStore_Ptr + (i * _singelItemDatalenght))); + } + + for (int i = 0; i < items.Count; i++) + { + ShopItem item = items[i]; + string ItemInfo; + if (item.UnKnow) + ItemInfo = $"{"0x" + item.Ptr.ToString("X") + ":"} | 解析失败"; + else + ItemInfo = $"{"0x" + item.Ptr.ToString("X") + ":"} | {item.ItemID} ({MHHelper.Get2MHFItemName(item.ItemID)}) | {item.Point}点 | {item.Group}({GetShopName(item.Group)}) {item.LevelType}({GetLevelTypeName(item.LevelType)})| [{item.OtherData[0].ToString("X")} {item.OtherData[1].ToString("X")} {item.OtherData[2].ToString("X")} {item.OtherData[3].ToString("X")}]"; + + OutInput.Add(ItemInfo); + outPutCsv.Add($"{item.ItemID},{item.Point},{item.Group},{item.LevelType}"); + Console.WriteLine(ItemInfo); + } + + /* + int TempPtr = 0x539A78; int ToUpCount = 712; for (int i = 0; i < ToUpCount; i++) { - items.Add(GetShopItemInfo(data, StartPtr + (-1 * i * _singelItemDatalenght))); + items.Add(GetShopItemInfo(data, TempPtr + (-1 * i * _singelItemDatalenght))); } for (int i = items.Count - 1; i >= 0; i--) @@ -93,10 +162,53 @@ namespace MHFOldShopTools if (item.UnKnow) ItemInfo = $"{"0x" + item.Ptr.ToString("X") + ":"} | 解析失败"; else - ItemInfo = $"{ "0x" + item.Ptr.ToString("X") + ":"} | {item.ItemID} ({MHHelper.Get2MHFItemName(item.ItemID)}) | {item.Point}点 | {item.Group}({GetShopName(item.Group)})| [{item.OtherData[0].ToString("X")} {item.OtherData[1].ToString("X")} {item.OtherData[2].ToString("X")} {item.OtherData[3].ToString("X")}]"; + ItemInfo = $"{ "0x" + item.Ptr.ToString("X") + ":"} | {item.ItemID} ({MHHelper.Get2MHFItemName(item.ItemID)}) | {item.Point}点 | {item.Group}({GetShopName(item.Group)}) {item.LevelType}({GetLevelTypeName(item.LevelType)})| [{item.OtherData[0].ToString("X")} {item.OtherData[1].ToString("X")} {item.OtherData[2].ToString("X")} {item.OtherData[3].ToString("X")}]"; Console.WriteLine(ItemInfo); } + */ + } + + static List LoadStructForCsv(string[] lines) + { + List itemList = new List(); + for (int i = 0;i < lines.Length; i++) + { + string[] temp = lines[i].Split(','); + ShopItem item = new ShopItem() + { + ItemID = Convert.ToInt32(temp[0]), + Point = Convert.ToInt32(temp[1]), + Group = Convert.ToInt32(temp[2]), + LevelType = Convert.ToInt32(temp[3]), + }; + itemList.Add(item); + } + return itemList; + } + + static void ModifyItem(byte[] srcdata, List items,out byte[] ResultData) + { + byte[] target = HexHelper.CopyByteArr(srcdata); + + //ClearData + for (int i = NPStore_Ptr; i < NPStore_Ptr + (NPStore_ItemCount * _singelItemDatalenght); i++) + target[i] = 0x00; + + for (int i = 0; i < items.Count; i++) + { + ShopItem itemdata = items[i]; + int tempItemIDPtr = NPStore_Ptr + (i * _singelItemDatalenght); + int tempPricePtr = tempItemIDPtr + 4; + int tempMenuPtr = tempItemIDPtr + 4 + 4; + int tempLevelPtr = tempItemIDPtr + 4 + 4 + 1; + HexHelper.ModifyDataToBytes(target, HexHelper.intToBytes(itemdata.ItemID), tempItemIDPtr); + HexHelper.ModifyDataToBytes(target, HexHelper.intToBytes(itemdata.Point), tempPricePtr); + target[tempMenuPtr] = (byte)itemdata.Group; + target[tempLevelPtr] = (byte)itemdata.LevelType; + } + + ResultData = target; } static string GetShopName(int gourp) @@ -113,18 +225,34 @@ namespace MHFOldShopTools return "未定义"; } } + + static string GetLevelTypeName(int Level) + { + switch (Level) + { + case 0:return "无限制"; + case 1:return "HR31以上"; + case 2:return "HR100以上"; + case 3:return "SR1以上"; + case 4:return "SR31以上"; + default: + return "未定义"; + } + } static ShopItem GetShopItemInfo(byte[] data, int StartPos) { int ItemID = -1; int Point = -1; int Group = -1; + int LevelType = -1; bool UnKnow = false; try { ItemID = HexHelper.bytesToInt(data, 4, StartPos); Point = HexHelper.bytesToInt(data, 4, StartPos + 4); Group = HexHelper.bytesToInt(data, 1, StartPos + 4 + 4); + LevelType = HexHelper.bytesToInt(data, 1, StartPos + 4 + 4 + 1); UnKnow = false; } catch(Exception ex) @@ -138,6 +266,7 @@ namespace MHFOldShopTools ItemID = ItemID, Point = Point, Group = Group, + LevelType = LevelType, OtherData = new int[]{ data[StartPos + 4 + 4 + 0],data[StartPos + 4 + 4 + 1], data[StartPos + 4 + 4 + 2], data[StartPos + 4 + 4 + 3] }, UnKnow = UnKnow }; @@ -150,6 +279,7 @@ namespace MHFOldShopTools public int ItemID; public int Point; public int Group; + public int LevelType; public int[] OtherData; public bool UnKnow; } diff --git a/README.md b/README.md new file mode 100644 index 0000000..493c9ae --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +##[Cn] + +本工具是用于Monster Hunter Frontier 老版本(Forward 5)的玩家道具商店读写工具。 + +因为老版本怪物猎人边境的NPC商店列表商店,并不受服务器控制。(如Erupe的shop相关的表) + +纯客户端文件固定死的。 + +为了便于修改,以及为了实现服务器控制客户端商店列表,实现一个工具或类库。于是有了本工具。 + +本工具,可以将mhfdat文件,解析后,将商店数据输出txt清单和csv文件。 + +同时,也可以自己编辑csv文件。用本工具,重新导入mhfdat。得以修复。 + +对非中国服务器的老版本服务器运营者(因为老版本MHF中国只有我一个运营的服务器),也分享本工具。 + +如果您的服务器有Launcher,那么可以通过集成本工具,最终由服务器控制客户端商店列表。(我服务器就是这么做的) + +** 目前只处理了,NetCafe Point Store这一个商店 + +##[En] +This tool is a player item store read-write tool for Monster Hunter Frontier older versions (Forward 5). + +Because the NPC store list store in the old version of Monster Hunter Frontier is not controlled by the server. (such as Erupe's shop related table) + +The pure client file is fixed. + +To facilitate modification and to achieve server control of the client store list, implement a tool or library. So there was this tool. + +This tool can parse the mhfdat file and output the store data as a txt list and CSV file. + +At the same time, you can also edit CSV files yourself. Use this tool to re import mhfdat. Can be repaired. + +I also share this tool with operators of older versions of non Chinese servers (as I am the only one operating the server in MHF China). + +If your server has a Launcher, then the client store list can be ultimately controlled by the server through the set cost tool. (That's how my server does it) + +** Currently, only the NetCafe Point Store has been processed