初版归档

This commit is contained in:
sin365 2025-08-13 16:52:38 +08:00
commit b6195c430d
13 changed files with 19801 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/.vs/
/*.csproj
/*.sln
/obj/
/bin/

64
Config.cs Normal file
View File

@ -0,0 +1,64 @@
namespace HaoYue.MHFUserSrv.Server.Common
{
public static class Config
{
public static Dictionary<string, string> DictCfg = new Dictionary<string, string>();
public static void LoadCfg(string file)
{
DictCfg = ReadCfg(file);
}
/// <summary>
/// 读取配置文件
/// </summary>
static Dictionary<string, string> ReadCfg(string file)
{
Dictionary<string, string> a = new Dictionary<string, string>()
{
{ "dbServer", string.Empty},
{ "dbPort", string.Empty},
{ "src_dbName", string.Empty},
{ "target_dbName", string.Empty},
{ "dbUser", string.Empty},
{ "dbPwd", string.Empty},
};
try
{
string cfg = string.Empty;
if (File.Exists(file))
{
cfg = File.ReadAllText(file);
}
if (!string.IsNullOrEmpty(cfg))
{
string[] b;
foreach (var kv in cfg.Split("\r\n", StringSplitOptions.RemoveEmptyEntries))
{
b = kv.Split('=', StringSplitOptions.RemoveEmptyEntries);
if (b.Length == 2)
{
if (a.ContainsKey(b[0]))
{
if (string.IsNullOrEmpty(b[1]))
{
continue;
}
else
{
a[b[0]] = b[1];
}
}
}
}
}
return a;
}
catch (Exception ex)
{
Console.WriteLine($"->ReadCfgCatch->{ex.Message}");
return a;
}
}
}
}

417
Data/DataStruct.cs Normal file
View File

@ -0,0 +1,417 @@
using Axibug.MHFSaveAutoConverter.Helper;
using System.Text;
using static Axibug.MHFSaveAutoConverter.DataStruct.DataStruct;
using static Axibug.MHFSaveAutoConverter.DataStruct.MHFSaveDataCfg;
namespace Axibug.MHFSaveAutoConverter.DataStruct
{
public static class DataStructSet
{
public static bool GetCfg(this s_Base entity, MHFVer ver, out EntityCfg cfg)
{
if (dictTypeWithCfg.TryGetValue(ver, out var dict) && dict.TryGetValue(entity.GetType().Name, out cfg))
return true;
else
{
cfg = null;
return false;
}
}
public static T Create<T>(MHFVer form, byte[] src) where T : s_Base, new()
{
T obj = new T();
obj.Load(form, src);
return obj;
}
}
public class EntityCfg
{
public int ptr = -1;
public int len = -1;
public int block_count = -1;
public int block_single_len = -1;
}
public class DataStruct
{
#region
public abstract class s_Base
{
public MHFVer SrcVer;
public EntityCfg SrcCfg;
public bool SrcVerHad => SrcCfg.ptr > 0;
public MHFVer TargetVer;
public EntityCfg TargetCfg;
public bool TargetHad => TargetCfg.ptr > 0;
public virtual bool Load(MHFVer from, byte[] src)
{
SrcVer = from;
this.GetCfg(from, out SrcCfg);
return SrcVerHad;
}
public virtual bool Write(MHFVer target, byte[] targetdata)
{
if (!SrcVerHad)
return false;
this.GetCfg(target, out TargetCfg);
return TargetHad;
}
public virtual string ToString()
{
return this.ToString();
}
}
public class s_base_Byte : s_Base
{
public byte data;
public override bool Load(MHFVer from, byte[] src)
{
if (!base.Load(from, src))
return false;
data = src[SrcCfg.ptr];
return true;
}
public override bool Write(MHFVer target, byte[] targetdata)
{
if (!base.Write(target, targetdata)) return false;
targetdata[TargetCfg.ptr] = data;
return true;
}
public override string ToString()
{
return $"{this.GetType().Name}:{data}";
}
public static EntityCfg CreateCfg(int _ptr) { return new EntityCfg() { ptr = _ptr }; }
}
public class s_base_Bytesarray : s_Base
{
public byte[] data;
public override bool Load(MHFVer from, byte[] src)
{
if (!base.Load(from, src)) return false;
data = new byte[SrcCfg.len];
for (int i = 0; i < SrcCfg.len; i++)
data[i] = src[SrcCfg.ptr + i];
return true;
}
public override bool Write(MHFVer target, byte[] targetdata)
{
if (!base.Write(target, targetdata)) return false;
int writeLenght = Math.Min(SrcCfg.len, TargetCfg.len);
writeLenght = Math.Min(writeLenght, data.Length);
for (int i = 0; i < writeLenght; i++)
targetdata[TargetCfg.ptr + i] = data[i];
return true;
}
public override string ToString()
{
return $"{this.GetType().Name}:Length:{(data != null ? data.Length : "")}";
}
public static EntityCfg CreateCfg(int _ptr, int _len) { return new EntityCfg() { ptr = _ptr, len = _len }; }
}
public class s_base_BytesarrayGroup : s_Base
{
public byte[] data;
public override bool Load(MHFVer from, byte[] src)
{
if (!base.Load(from, src)) return false;
List<byte> temp = new List<byte>();
int alllen = SrcCfg.block_count * SrcCfg.block_single_len;
data = new byte[alllen];
for (int i = 0; i < alllen; i++)
temp.Add(src[SrcCfg.ptr + i]);
data = temp.ToArray();
return true;
}
public override bool Write(MHFVer target, byte[] targetdata)
{
if (!base.Write(target, targetdata)) return false;
int srclen = SrcCfg.block_count * SrcCfg.block_single_len;
int targetlen = TargetCfg.block_count * TargetCfg.block_single_len;
int writeLenght = Math.Min(srclen, targetlen);
writeLenght = Math.Min(writeLenght, data.Length);
for (int i = 0; i < writeLenght; i++)
targetdata[TargetCfg.ptr + i] = data[i];
return true;
}
public override string ToString()
{
return $"{this.GetType().Name}:Length:{(data != null ? data.Length : "")}";
}
public static EntityCfg CreateCfg(int _ptr, int _block_single_len, int _block_count) { return new EntityCfg() { ptr = _ptr, block_count = _block_count, block_single_len = _block_single_len }; }
}
public class s_4byte_UInt32_Base : s_base_Bytesarray
{
public override string ToString()
{
return $"{this.GetType().Name}:{(data != null ? HexHelper.bytesToUInt(data, data.Length, 0).ToString() : "")}";
}
public static EntityCfg CreateCfg(int _ptr) { return new EntityCfg() { ptr = _ptr, len = 4 }; }
}
public class s_2byte_Int16_Base : s_base_Bytesarray
{
public override string ToString()
{
return $"{this.GetType().Name}:{(data != null ? HexHelper.bytesToUInt(data, data.Length, 0).ToString() : "")}";
}
public static EntityCfg CreateCfg(int _ptr) { return new EntityCfg() { ptr = _ptr, len = 2 }; }
}
public class s_String_Base : s_Base
{
public byte[] data;
public string strdata;
public override bool Load(MHFVer from, byte[] src)
{
if (!base.Load(from, src)) return false;
List<byte> temp = new List<byte>();
int ptr = SrcCfg.ptr;
for (int i = 0; i < SrcCfg.len; i++)
{
if (src[ptr + i] == 0x00 || src[ptr + i] == 0xFF)
{
temp.Add(src[ptr + i]);
break;
}
temp.Add(src[ptr + i]);
}
data = temp.ToArray();
strdata = Encoding.GetEncoding("shift-jis").GetString(data);
return true;
}
public override bool Write(MHFVer target, byte[] targetdata)
{
if (!base.Write(target, targetdata)) return false;
int writeLenght = Math.Min(SrcCfg.len, TargetCfg.len);
writeLenght = Math.Min(writeLenght, data.Length);
for (int i = 0; i < writeLenght; i++)
targetdata[TargetCfg.ptr + i] = data[i];
return true;
}
public override string ToString()
{
return strdata;
}
public static EntityCfg CreateCfg(int _ptr, int _len) { return new EntityCfg() { ptr = _ptr, len = _len }; }
}
#endregion
public class s_RoleHandleGroup : s_base_Bytesarray { }
public class s_Gender : s_base_Byte
{
public enum E_SEX : byte
{
M,
W
}
public E_SEX sex => (E_SEX)data;
public override string ToString()
{
return sex.ToString();
}
}
public class s_Name : s_String_Base
{
public override string ToString()
{
return strdata;
}
}
public class s_Zenny : s_4byte_UInt32_Base { }
public class s_HRP : s_4byte_UInt32_Base { }
public class s_EquipmentBox : s_base_BytesarrayGroup
{
public override string ToString()
{
if (data == null) return base.ToString();
string str = string.Empty;
List<uint> idlistdata = new List<uint>();
for (int block = 0; block < SrcCfg.block_count; block++)
{
int startptr = (block * SrcCfg.block_single_len);
byte[] itemiddata = new byte[2];
for (int i = 0; i < SrcCfg.block_single_len; i++)
{
if (i == 0) itemiddata[0] = data[startptr + i];
if (i == 1) itemiddata[1] = data[startptr + i];
}
idlistdata.Add(HexHelper.bytesToUInt(itemiddata, 2, 0));
}
foreach (var item in idlistdata)
str += $"装备ID{item}\r\n";
return str;
}
}
public class s_Itembox : s_base_BytesarrayGroup
{
public override string ToString()
{
string str = string.Empty;
byte[] itemiddata = new byte[2];
byte[] countdata = new byte[2];
List<(int, int)> itemdata = new List<(int, int)>();
for (int block = 0; block < SrcCfg.block_count; block++)
{
int startptr = (block * SrcCfg.block_single_len);
for (int i = 0; i < SrcCfg.block_single_len; i++)
{
if (i == 4) itemiddata[0] = data[startptr + i];
if (i == 5) itemiddata[1] = data[startptr + i];
if (i == 6) countdata[0] = data[startptr + i];
if (i == 7) countdata[1] = data[startptr + i];
}
itemdata.Add((HexHelper.bytesToInt(itemiddata, 2, 0), HexHelper.bytesToInt(countdata, 2, 0)));
}
foreach (var item in itemdata)
str += $"{MHHelper.Get2MHFItemName(item.Item1)}:{item.Item2}\r\n";
return str;
}
}
public class s_pPlaytime : s_4byte_UInt32_Base
{
public override string ToString()
{
return $"游戏时长:{this.GetType().Name}:{(data != null ? (HexHelper.bytesToInt(data, data.Length, 0) / 3600) + "" : "")}";
}
}
public class s_CurrentEquip : s_base_BytesarrayGroup
{
}
public class s_pStandardInfoSub1 : s_base_Bytesarray { }
public class s_pWeaponID : s_2byte_Int16_Base { }
public class s_pWeaponType : s_base_Byte { };
public class s_KillMonsterCountArr : s_base_BytesarrayGroup { };
public class s_pWeaponTypeGroup : s_base_Bytesarray { };
public class s_pHouseTier : s_4byte_UInt32_Base { }
public class s_ItemPresets_Names : s_base_BytesarrayGroup
{
public override string ToString()
{
return "道具预设名称:" + Encoding.GetEncoding("shift-jis").GetString(data);
}
}
public class s_ItemPresets_Ids : s_base_BytesarrayGroup
{
public override string ToString()
{
List<uint> idlistdata = new List<uint>();
for (int block = 0; block < SrcCfg.block_count; block++)
{
int startptr = (block * SrcCfg.block_single_len);
byte[] itemiddata = new byte[2];
for (int i = 0; i < SrcCfg.block_single_len; i++)
{
if (i == 0) itemiddata[0] = data[startptr + i];
if (i == 1) itemiddata[1] = data[startptr + i];
}
idlistdata.Add(HexHelper.bytesToUInt(itemiddata, 2, 0));
}
string str = "道具预设道具ID";
foreach (var item in idlistdata)
str += $"{item}{MHHelper.Get2MHFItemName((int)item)}\r\n";
return str;
}
}
public class s_SR_片手 : s_4byte_UInt32_Base { }
public class s_SR_双刀 : s_4byte_UInt32_Base { }
public class s_SR_大剑 : s_4byte_UInt32_Base { }
public class s_SR_太刀 : s_4byte_UInt32_Base { }
public class s_SR_锤子 : s_4byte_UInt32_Base { }
public class s_SR_笛 : s_4byte_UInt32_Base { }
public class s_SR_长枪 : s_4byte_UInt32_Base { }
public class s_SR_铳枪 : s_4byte_UInt32_Base { }
public class s_SR_穿龙棍 : s_4byte_UInt32_Base { }
public class s_SR_轻弩 : s_4byte_UInt32_Base { }
public class s_SR_重弩 : s_4byte_UInt32_Base { }
public class s_SR_弓 : s_4byte_UInt32_Base { }
public class s_SR_片手_Status1 : s_base_Byte{}
public class s_SR_双刀_Status1 :s_base_Byte{}
public class s_SR_大剑_Status1 :s_base_Byte{}
public class s_SR_太刀_Status1 :s_base_Byte{}
public class s_SR_锤子_Status1 :s_base_Byte{}
public class s_SR_笛_Status1 :s_base_Byte{}
public class s_SR_长枪_Status1 :s_base_Byte{}
public class s_SR_铳枪_Status1 :s_base_Byte{}
public class s_SR_穿龙棍_Status1 :s_base_Byte{}
public class s_SR_轻弩_Status1 :s_base_Byte{}
public class s_SR_重弩_Status1 :s_base_Byte{}
public class s_SR_弓_Status1 : s_base_Byte {}
public class s_SR_片手_Status2 : s_base_Byte { }
public class s_SR_双刀_Status2 : s_base_Byte { }
public class s_SR_大剑_Status2 : s_base_Byte { }
public class s_SR_太刀_Status2 : s_base_Byte { }
public class s_SR_锤子_Status2 : s_base_Byte { }
public class s_SR_笛_Status2 : s_base_Byte { }
public class s_SR_长枪_Status2 : s_base_Byte { }
public class s_SR_铳枪_Status2 : s_base_Byte { }
public class s_SR_穿龙棍_Status2 : s_base_Byte { }
public class s_SR_轻弩_Status2 : s_base_Byte { }
public class s_SR_重弩_Status2 : s_base_Byte { }
public class s_SR_弓_Status2 : s_base_Byte { }
public class s_ItemPresets_counts : s_base_BytesarrayGroup
{
public override string ToString()
{
List<uint> idlistdata = new List<uint>();
for (int block = 0; block < SrcCfg.block_count; block++)
{
int startptr = (block * SrcCfg.block_single_len);
byte[] itemiddata = new byte[2];
for (int i = 0; i < SrcCfg.block_single_len; i++)
{
if (i == 0) itemiddata[0] = data[startptr + i];
if (i == 1) itemiddata[1] = data[startptr + i];
}
idlistdata.Add(HexHelper.bytesToUInt(itemiddata, 2, 0));
}
string str = "道具预设道具数量:";
foreach (var item in idlistdata)
str += $"{item}{MHHelper.Get2MHFItemName((int)item)}\r\n";
return str;
}
}
public class s_pToreData : s_base_Bytesarray { }
public class s_pHR : s_4byte_UInt32_Base { }
public class s_pGRP : s_4byte_UInt32_Base { }
public class s_pHouseData : s_base_Bytesarray { }
public class s_Gzenn : s_4byte_UInt32_Base { }
public class s_Stylevouchers : s_4byte_UInt32_Base { }
public class s_Dailyguild : s_4byte_UInt32_Base { }
public class s_Socialize : s_base_Bytesarray
{
public override string ToString()
{
return "社交数据区块:" + base.ToString();
}
}
public class s_CP : s_4byte_UInt32_Base { }
public class s_知名度 : s_4byte_UInt32_Base { }
public class s_知名度称号 : s_base_Byte { }
public class s_pBookshelfData : s_base_Bytesarray { }
public class s_pGalleryData : s_base_Bytesarray { }
public class s_pGardenData花园 : s_base_Bytesarray { }
public class s_pRP : s_2byte_Int16_Base { }
public class s_pKQF : s_base_Bytesarray { }
public class s_ItemPouch背包 : s_base_BytesarrayGroup { }
public class s_Keyquestflag : s_base_Bytesarray { }
}
}

118
Data/MHFCompression.cs Normal file
View File

@ -0,0 +1,118 @@
namespace Axibug.MHFSaveAutoConverter.DataStruct
{
public static class MHFCompression
{
// 文件头常量
private static readonly byte[] HeaderBytes = {
0x63, 0x6D, 0x70, 0x20, // "cmp "
0x32, 0x30, 0x31, 0x31, 0x30, 0x31, 0x31, 0x33, // "20110113"
0x20, 0x20, 0x20, 0x00 // " \0"
};
public static byte[] Decompress(byte[] data)
{
// 检查文件头
if (data.Length < 16 || !data.Take(16).SequenceEqual(HeaderBytes))
{
Console.WriteLine("存档存档无需解压");
return data;
}
Console.WriteLine("存档解压");
using var outputStream = new MemoryStream();
using var reader = new BinaryReader(new MemoryStream(data));
// 跳过16字节头
reader.BaseStream.Seek(16, SeekOrigin.Begin);
while (reader.BaseStream.Position < reader.BaseStream.Length)
{
byte currentByte = reader.ReadByte();
if (currentByte == 0x00)
{
// 读取零序列长度
int nullCount = reader.ReadByte();
// 写入相应数量的零
for (int i = 0; i < nullCount; i++)
{
outputStream.WriteByte(0x00);
}
}
else
{
// 直接写入非零字节
outputStream.WriteByte(currentByte);
}
}
return outputStream.ToArray();
}
public static byte[] Compress(byte[] data)
{
using var outputStream = new MemoryStream();
using var writer = new BinaryWriter(outputStream);
// 写入文件头
writer.Write(HeaderBytes);
using var reader = new BinaryReader(new MemoryStream(data));
int nullCount = 0;
while (reader.BaseStream.Position < reader.BaseStream.Length)
{
byte currentByte = reader.ReadByte();
if (currentByte == 0x00)
{
nullCount++;
// 处理连续零的序列
while (nullCount < 255 && reader.BaseStream.Position < reader.BaseStream.Length)
{
byte nextByte = reader.ReadByte();
if (nextByte == 0x00)
{
nullCount++;
}
else
{
// 写入压缩标记和零计数
writer.Write((byte)0x00);
writer.Write((byte)nullCount);
// 写入遇到的非零字节
writer.Write(nextByte);
nullCount = 0;
break;
}
}
// 处理达到最大计数或文件结束的情况
if (nullCount > 0)
{
writer.Write((byte)0x00);
writer.Write((byte)Math.Min(nullCount, 255));
nullCount = 0;
}
}
else
{
// 直接写入非零字节
writer.Write(currentByte);
}
}
// 处理结尾可能的剩余零序列
if (nullCount > 0)
{
writer.Write((byte)0x00);
writer.Write((byte)nullCount);
}
return outputStream.ToArray();
}
}
}

181
Data/MHFSaveDataCfg.cs Normal file
View File

@ -0,0 +1,181 @@
using System.Text;
using static Axibug.MHFSaveAutoConverter.DataStruct.DataStruct;
namespace Axibug.MHFSaveAutoConverter.DataStruct
{
public static class MHFSaveDataCfg
{
public static Dictionary<MHFVer, Dictionary<string, EntityCfg>> dictTypeWithCfg = new Dictionary<MHFVer, Dictionary<string, EntityCfg>>()
{
{
MHFVer.FW5,new Dictionary<string, EntityCfg>()
{
{nameof(s_RoleHandleGroup),s_RoleHandleGroup.CreateCfg(0x50,0x110-0x50)},//前面都是连续数据
{nameof(s_Gender),s_Gender.CreateCfg(0x50)},
{nameof(s_Name),s_Name.CreateCfg(0x58,50)},
{nameof(s_HRP),s_HRP.CreateCfg(0xAC)},
{nameof(s_Zenny),s_Zenny.CreateCfg(0xb0)},
{nameof(s_EquipmentBox),s_EquipmentBox.CreateCfg(0x110,16,100 * 20)},
//{nameof(s_Itembox),s_Itembox.CreateCfg(0x7E10,8,35 * 100)},
{nameof(s_Itembox),s_Itembox.CreateCfg(0x7E24,8,35 * 100)},
{nameof(s_pPlaytime),s_pPlaytime.CreateCfg(0xEBC4)},
{nameof(s_CurrentEquip),s_CurrentEquip.CreateCfg(0xEC54,6,16)},
//{nameof(s_pStandardInfoSub1),s_pStandardInfoSub1.CreateCfg(0xEC6A,1398)},
{nameof(s_pWeaponID),s_pWeaponID.CreateCfg(0xEC6A)},
{nameof(s_pWeaponTypeGroup),s_pWeaponTypeGroup.CreateCfg(0xED70,24)},
{nameof(s_pStandardInfoSub1),s_pStandardInfoSub1.CreateCfg(0xED75,0xF1E0 - 0xED75)},
{nameof(s_pWeaponType),s_pWeaponType.CreateCfg(0xED75)},
{nameof(s_KillMonsterCountArr),s_KillMonsterCountArr.CreateCfg(0xEFBE,2,212)},
{nameof(s_pHouseTier),s_pHouseTier.CreateCfg(0xF1CC)},
{nameof(s_ItemPresets_Names),s_ItemPresets_Names.CreateCfg(0xF1E0,32,24)},
{nameof(s_ItemPresets_Ids),s_ItemPresets_Ids.CreateCfg(0xF3C0,2,30)},
{nameof(s_SR_片手),s_SR_片手.CreateCfg(0xF624)},
{nameof(s_SR_双刀),s_SR_双刀.CreateCfg(0xF628)},
{nameof(s_SR_大剑),s_SR_大剑.CreateCfg(0xF62C)},
{nameof(s_SR_太刀),s_SR_太刀.CreateCfg(0xF630)},
{nameof(s_SR_锤子),s_SR_锤子.CreateCfg(0xF634)},
{nameof(s_SR_笛),s_SR_笛.CreateCfg(0xF638)},
{nameof(s_SR_长枪),s_SR_长枪.CreateCfg(0xF63C)},
{nameof(s_SR_铳枪),s_SR_铳枪.CreateCfg(0xF640)},
{nameof(s_SR_穿龙棍),s_SR_穿龙棍.CreateCfg(-1)},
{nameof(s_SR_轻弩),s_SR_轻弩.CreateCfg(0xF644)},
{nameof(s_SR_重弩),s_SR_重弩.CreateCfg(0xF648)},
{nameof(s_SR_弓),s_SR_弓.CreateCfg(0xF64C)},
{nameof(s_SR_片手_Status1),s_SR_片手_Status1.CreateCfg(0xF650)},
{nameof(s_SR_双刀_Status1),s_SR_双刀_Status1.CreateCfg(0xF650+(1*1))},
{nameof(s_SR_大剑_Status1),s_SR_大剑_Status1.CreateCfg(0xF650+(1*2))},
{nameof(s_SR_太刀_Status1),s_SR_太刀_Status1.CreateCfg(0xF650+(1*3))},
{nameof(s_SR_锤子_Status1),s_SR_锤子_Status1.CreateCfg(0xF650+(1*4))},
{nameof(s_SR_笛_Status1),s_SR_笛_Status1.CreateCfg(0xF650+(1*5))},
{nameof(s_SR_长枪_Status1),s_SR_长枪_Status1.CreateCfg(0xF650+(1*6))},
{nameof(s_SR_铳枪_Status1),s_SR_铳枪_Status1.CreateCfg(0xF650+(1*7))},
{nameof(s_SR_穿龙棍_Status1),s_SR_穿龙棍_Status1.CreateCfg(-1)},
{nameof(s_SR_轻弩_Status1),s_SR_轻弩_Status1.CreateCfg(0xF650+(1*8))},
{nameof(s_SR_重弩_Status1),s_SR_重弩_Status1.CreateCfg(0xF650+(1*9))},
{nameof(s_SR_弓_Status1),s_SR_弓_Status1.CreateCfg(0xF650+(1*10)) },
{nameof(s_SR_片手_Status2),s_SR_片手_Status2.CreateCfg(0xF65B)},
{nameof(s_SR_双刀_Status2),s_SR_双刀_Status2.CreateCfg(0xF65B+(1*1))},
{nameof(s_SR_大剑_Status2),s_SR_大剑_Status2.CreateCfg(0xF65B+(1*2))},
{nameof(s_SR_太刀_Status2),s_SR_太刀_Status2.CreateCfg(0xF65B+(1*3))},
{nameof(s_SR_锤子_Status2),s_SR_锤子_Status2.CreateCfg(0xF65B+(1*4))},
{nameof(s_SR_笛_Status2),s_SR_笛_Status2.CreateCfg(0xF65B+(1*5))},
{nameof(s_SR_长枪_Status2),s_SR_长枪_Status2.CreateCfg(0xF65B+(1*6))},
{nameof(s_SR_铳枪_Status2),s_SR_铳枪_Status2.CreateCfg(0xF65B+(1*7))},
{nameof(s_SR_穿龙棍_Status2),s_SR_穿龙棍_Status2.CreateCfg(-1)},
{nameof(s_SR_轻弩_Status2),s_SR_轻弩_Status2.CreateCfg(0xF65B+(1*8))},
{nameof(s_SR_重弩_Status2),s_SR_重弩_Status2.CreateCfg(0xF65B+(1*9))},
{nameof(s_SR_弓_Status2),s_SR_弓_Status2.CreateCfg(0xF65B+(1*10)) },
{nameof(s_ItemPresets_counts),s_ItemPresets_counts.CreateCfg(0xF960,1,30)},
{nameof(s_pToreData),s_pToreData.CreateCfg(0xF314,240)},
{nameof(s_pHR),s_pHR.CreateCfg(0xF456)},
{nameof(s_pGRP),s_pGRP.CreateCfg(-1)},
{nameof(s_pHouseData),s_pHouseData.CreateCfg(0xF461,195)},
{nameof(s_Gzenn) ,s_Gzenn.CreateCfg(-1)},
{nameof(s_Stylevouchers) ,s_Stylevouchers.CreateCfg(-1)},
{nameof(s_Socialize) ,s_Socialize.CreateCfg(0x1084C,0x10E30 - 0x1084C)},//社交数据
{nameof(s_CP) ,s_CP.CreateCfg(0x10944)},
{nameof(s_知名度) ,s_知名度.CreateCfg(0x10948)},
{nameof(s_知名度称号) ,s_知名度称号.CreateCfg(0x1094C)},
{nameof(s_pBookshelfData),s_pBookshelfData.CreateCfg(-1,-1)},
{nameof(s_pGalleryData),s_pGalleryData.CreateCfg(0x11980,1748)},
{nameof(s_pGardenData花园),s_pGardenData花园.CreateCfg(0x122B8,68)},
{nameof(s_pRP),s_pRP.CreateCfg(0x12376)},
{nameof(s_pKQF),s_pKQF.CreateCfg(-1,8)},
/*单个道具长度8byte前20个是item/道具 后10个是ammo/弹药*/
{nameof(s_ItemPouch背包),s_ItemPouch背包.CreateCfg(-1,8,20+10)},
{nameof(s_Keyquestflag),s_Keyquestflag.CreateCfg(-1,8) },
}
},
{
MHFVer.GG,new Dictionary<string, EntityCfg>()
{
{nameof(s_RoleHandleGroup),s_RoleHandleGroup.CreateCfg(0x50,0x110-0x50)},//前面都是连续数据
{nameof(s_Gender),s_Gender.CreateCfg(0x50)},
{nameof(s_Name),s_Name.CreateCfg(0x58,50)},
{nameof(s_HRP),s_HRP.CreateCfg(0xAC)},
{nameof(s_Zenny),s_Zenny.CreateCfg(0xb0)},
{nameof(s_EquipmentBox),s_EquipmentBox.CreateCfg(0x110,16,100 * 20)},
{nameof(s_Itembox),s_Itembox.CreateCfg(0xBCA4,8,35 * 100)},
{nameof(s_pPlaytime),s_pPlaytime.CreateCfg(0x168C4)},
{nameof(s_CurrentEquip),s_CurrentEquip.CreateCfg(0x16960,6,16)},
//{nameof(s_pStandardInfoSub1),s_pStandardInfoSub1.CreateCfg(0x1696A,1398)},
{nameof(s_pWeaponID),s_pWeaponID.CreateCfg(0x1696A)},
{nameof(s_pWeaponTypeGroup),s_pWeaponTypeGroup.CreateCfg(0x16A70,24)},
{nameof(s_pStandardInfoSub1),s_pStandardInfoSub1.CreateCfg(0x16A75,0x16EE0-0x16A75)},
{nameof(s_pWeaponType),s_pWeaponType.CreateCfg(0x16A75)},
{nameof(s_KillMonsterCountArr),s_KillMonsterCountArr.CreateCfg(0x16CBE,2,212)},//当然GG不可能只有212个怪物
{nameof(s_pHouseTier),s_pHouseTier.CreateCfg(0x16ECC)},
{nameof(s_ItemPresets_Names),s_ItemPresets_Names.CreateCfg(0x16EE0,32,24)},
{nameof(s_ItemPresets_Ids),s_ItemPresets_Ids.CreateCfg(0x170C0,2,30)},
{nameof(s_SR_片手),s_SR_片手.CreateCfg(0x1A374)},
{nameof(s_SR_双刀),s_SR_双刀.CreateCfg(0X1A378)},
{nameof(s_SR_大剑),s_SR_大剑.CreateCfg(0x1A37C)},
{nameof(s_SR_太刀),s_SR_太刀.CreateCfg(0x1A380)},
{nameof(s_SR_锤子),s_SR_锤子.CreateCfg(0x1A384)},
{nameof(s_SR_笛),s_SR_笛.CreateCfg(0x1A388)},
{nameof(s_SR_长枪),s_SR_长枪.CreateCfg(0x1A38C)},
{nameof(s_SR_铳枪),s_SR_铳枪.CreateCfg(0x1A390)},
{nameof(s_SR_穿龙棍),s_SR_穿龙棍.CreateCfg(0x1A394)},
{nameof(s_SR_轻弩),s_SR_轻弩.CreateCfg(0x1A398)},
{nameof(s_SR_重弩),s_SR_重弩.CreateCfg(0X1A39C)},
{nameof(s_SR_弓),s_SR_弓.CreateCfg(0x1A3A0)},
{nameof(s_SR_片手_Status1),s_SR_片手_Status1.CreateCfg(0x1A3A4)},
{nameof(s_SR_双刀_Status1),s_SR_双刀_Status1.CreateCfg(0x1A3A4+(1*1))},
{nameof(s_SR_大剑_Status1),s_SR_大剑_Status1.CreateCfg(0x1A3A4+(1*2))},
{nameof(s_SR_太刀_Status1),s_SR_太刀_Status1.CreateCfg(0x1A3A4+(1*3))},
{nameof(s_SR_锤子_Status1),s_SR_锤子_Status1.CreateCfg(0x1A3A4+(1*4))},
{nameof(s_SR_笛_Status1),s_SR_笛_Status1.CreateCfg(0x1A3A4+(1*5))},
{nameof(s_SR_长枪_Status1),s_SR_长枪_Status1.CreateCfg(0x1A3A4+(1*6))},
{nameof(s_SR_铳枪_Status1),s_SR_铳枪_Status1.CreateCfg(0x1A3A4+(1*7))},
{nameof(s_SR_穿龙棍_Status1),s_SR_穿龙棍_Status1.CreateCfg(0x1A3A4+(1*8))},
{nameof(s_SR_轻弩_Status1),s_SR_轻弩_Status1.CreateCfg(0x1A3A4+(1*9))},
{nameof(s_SR_重弩_Status1),s_SR_重弩_Status1.CreateCfg(0x1A3A4+(1*10))},
{nameof(s_SR_弓_Status1),s_SR_弓_Status1.CreateCfg(0x1A3A4+(1*11)) },
{nameof(s_SR_片手_Status2),s_SR_片手_Status2.CreateCfg(0x1A3B0)},
{nameof(s_SR_双刀_Status2),s_SR_双刀_Status2.CreateCfg(0x1A3B0+(1*1))},
{nameof(s_SR_大剑_Status2),s_SR_大剑_Status2.CreateCfg(0x1A3B0+(1*2))},
{nameof(s_SR_太刀_Status2),s_SR_太刀_Status2.CreateCfg(0x1A3B0+(1*3))},
{nameof(s_SR_锤子_Status2),s_SR_锤子_Status2.CreateCfg(0x1A3B0+(1*4))},
{nameof(s_SR_笛_Status2),s_SR_笛_Status2.CreateCfg(0x1A3B0+(1*5))},
{nameof(s_SR_长枪_Status2),s_SR_长枪_Status2.CreateCfg(0x1A3B0+(1*6))},
{nameof(s_SR_铳枪_Status2),s_SR_铳枪_Status2.CreateCfg(0x1A3B0+(1*7))},
{nameof(s_SR_穿龙棍_Status2),s_SR_穿龙棍_Status2.CreateCfg(0x1A3B0+(1*8))},
{nameof(s_SR_轻弩_Status2),s_SR_轻弩_Status2.CreateCfg(0x1A3B0+(1*9))},
{nameof(s_SR_重弩_Status2),s_SR_重弩_Status2.CreateCfg(0x1A3B0+(1*10))},
{nameof(s_SR_弓_Status2),s_SR_弓_Status2.CreateCfg(0x1A3B0+(1*11)) },
{nameof(s_ItemPresets_counts),s_ItemPresets_counts.CreateCfg(0x17660,1,30)},
{nameof(s_pToreData),s_pToreData.CreateCfg(0x17014,240)},
{nameof(s_pHR),s_pHR.CreateCfg(0x17156)},
{nameof(s_pGRP),s_pGRP.CreateCfg(0x1715C)},
{nameof(s_pHouseData),s_pHouseData.CreateCfg(0x17161,195)},
{nameof(s_Gzenn) ,s_Gzenn.CreateCfg(0x172C4)},
{nameof(s_Stylevouchers) ,s_Stylevouchers.CreateCfg(-1)},
{nameof(s_Socialize) ,s_Socialize.CreateCfg(0x1854C,0x18B30 - 0x1854C)},//社交数据
{nameof(s_CP) ,s_CP.CreateCfg(0x18644)},
{nameof(s_知名度) ,s_知名度.CreateCfg(0x18648)},
{nameof(s_知名度称号) ,s_知名度称号.CreateCfg(0x1864C)},
{nameof(s_pBookshelfData),s_pBookshelfData.CreateCfg(0x11A8,-1)},
{nameof(s_pGalleryData),s_pGalleryData.CreateCfg(0x19680,1748)},
{nameof(s_pGardenData花园),s_pGardenData花园.CreateCfg(0x19FB8,68)},
{nameof(s_pRP),s_pRP.CreateCfg(0x1A076)},
{nameof(s_pKQF),s_pKQF.CreateCfg(0x1B080,8)},
/*单个道具长度8byte前20个是item/道具 后10个是ammo/弹药*/
{nameof(s_ItemPouch背包),s_ItemPouch背包.CreateCfg(-1,8,20+10)},
{nameof(s_Keyquestflag),s_Keyquestflag.CreateCfg(-1,8) },
}
}
};
public enum MHFVer
{
FW5,
GG
}
}
}

64
Data/SaveDataCoverter.cs Normal file
View File

@ -0,0 +1,64 @@
using Axibug.MHFSaveAutoConverter.DataStruct;
using Axibug.MHFSaveAutoConverter.SQL;
using System.Data;
using System.Text;
using static Axibug.MHFSaveAutoConverter.DataStruct.MHFSaveDataCfg;
namespace Axibug.MHFSaveAutoConverter.Data
{
public class SaveDataCoverter
{
public static bool loadCharacterOLD(long cid, out string name, out bool is_female, out byte[] data)
{
string sql = $"SELECT \"name\",savedata,is_female from \"characters\" WHERE id = {cid}";
data = null;
name = default;
is_female = default;
if (SQLRUN_SRC_DB.QuerySQL(sql, out DataTable dt))
{
name = string.Empty;
DataRowCollection RowsData = dt.Rows;
if (RowsData.Count > 0)
{
name = (RowsData[0][0]).ToString();
data = (byte[])(RowsData[0][1]);
is_female = Convert.ToBoolean(RowsData[0][2]);
return true;
}
else
Console.WriteLine($"未查询到数据");
}
return false;
}
public static bool ConvertSaveData(MHFVer from, MHFVer target, long srccid, string name, bool onlydumpsrc, byte[] src, out byte[] targetdata, out string err)
{
try
{
string path = $"src_{from}_cid_{srccid}_{DateTime.Now.ToString("yyyyMMddHHmmss")}" + "_decrypt.bin";
byte[] decdata = MHFCompression.Decompress(src);
System.IO.File.WriteAllBytes(path, decdata);
Console.WriteLine($"{from}_{srccid}_角色数据{name},已保存数据到:{path}");
if (onlydumpsrc)
{
err = default;
targetdata = default;
return true;
}
SaveDataEntity se = new SaveDataEntity(from, target, decdata);
string updateoutpath = $"update_{target}_cid_{srccid}_{DateTime.Now.ToString("yyyyMMddHHmmss")}" + "_fixed.bin";
targetdata = se.DoConvert();
System.IO.File.WriteAllBytes(updateoutpath, targetdata);
Console.WriteLine($"{target}_{srccid}_角色数据{name},已保存数据到:{updateoutpath}");
err = default;
return true;
}
catch (Exception ex)
{
err = ex.ToString();
targetdata = default;
return false;
}
}
}
}

75
Data/SaveDataEntity.cs Normal file
View File

@ -0,0 +1,75 @@
using Axibug.MHFSaveAutoConverter.SQL;
using System.Data;
using static Axibug.MHFSaveAutoConverter.DataStruct.DataStruct;
using static Axibug.MHFSaveAutoConverter.DataStruct.MHFSaveDataCfg;
namespace Axibug.MHFSaveAutoConverter.DataStruct
{
public class SaveDataEntity
{
public MHFVer FromVer;
public MHFVer TargetVer;
public List<s_Base> saveHandles = new List<s_Base>();
public SaveDataEntity(MHFVer from, MHFVer target, byte[] data)
{
FromVer = from;
TargetVer = target;
string[] nameArr = dictTypeWithCfg[from].Keys.ToArray();
foreach (string className in nameArr)
{
string cName = typeof(DataStruct).FullName + "+" + className;
try
{
Type type = Type.GetType(cName);
if (type == null)
{
Console.WriteLine($"类型 {className} 未找到");
continue;
}
s_Base instance = (s_Base)Activator.CreateInstance(type);
saveHandles.Add(instance);
instance.Load(from, data);
}
catch (Exception ex)
{
Console.WriteLine($"处理 {className} 时出错: {ex.Message}");
}
}
Console.WriteLine("====读取====");
foreach (var singledata in saveHandles)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(singledata.GetType().Name);
Console.ForegroundColor = ConsoleColor.White;
string str = singledata.ToString();
if (str.Length > 100)
str = str.Substring(0, 100) + "...";
Console.WriteLine(str);
}
}
public byte[] DoConvert()
{
Console.WriteLine("====读取模板数据====");
byte[] data = File.ReadAllBytes("./savetemplete.bin");
Console.WriteLine("====尝试开始写入====");
foreach (var singledata in saveHandles)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(singledata.GetType().Name);
Console.ForegroundColor = ConsoleColor.White;
string str = singledata.ToString();
if (str.Length > 100)
str = str.Substring(0, 100) + "...";
bool ret = singledata.Write(TargetVer, data);
Console.WriteLine($"写入:{singledata.GetType().Name} =>{(ret ? "" : "")}");
Console.WriteLine(str);
}
Console.WriteLine("====写入完毕====");
return data;
}
}
}

250
Helper/HexHelper.cs Normal file
View File

@ -0,0 +1,250 @@
using System.Text;
namespace Axibug.MHFSaveAutoConverter.Helper
{
public class HexHelper
{
public static byte[] CopyByteArr(byte[] src)
{
byte[] target = new byte[src.Length];
//加载数据
target = new byte[src.Length];
for (int i = 0; i < src.Length; i++)
target[i] = src[i];
return target;
}
/// <summary>
/// 读取byte[]数据
/// </summary>
/// <param name="src"></param>
/// <param name="lenght"></param>
/// <param name="offset"></param>
/// <returns></returns>
public static byte[] ReadBytes(byte[] src, int lenght, int offset = 0)
{
byte[] data = new byte[lenght];
for (int i = 0; i < lenght; i++)
{
data[i] = src[offset + i];
}
return data;
}
/**
* byte[]int byte高位在前
*/
public static int bytesToInt(byte[] src, int lenght, int offset = 0)
{
if (lenght == 1)
return src[offset + 0];
byte[] data = new byte[lenght];
for (int i = 0; i < lenght; i++)
{
data[i] = src[offset + i];
}
if (lenght == 2)
return BitConverter.ToInt16(data, 0);
else //if (lenght == 4)
return BitConverter.ToInt32(data, 0);
}
/**
* byte[]int byte高位在前
*/
public static uint bytesToUInt(byte[] src, int lenght, int offset = 0)
{
if (lenght == 1)
return src[offset + 0];
byte[] data = new byte[lenght];
for (int i = 0; i < lenght; i++)
{
data[i] = src[offset + i];
}
if (lenght == 2)
return BitConverter.ToUInt16(data, 0);
else //if (lenght == 4)
return BitConverter.ToUInt32(data, 0);
}
/**
* int byte[] byte高位在前
*/
public static byte[] intToBytes(int value)
{
return BitConverter.GetBytes(value);
}
/**
* int byte[] byte高位在前
*/
public static byte[] uintToBytes(uint value)
{
return BitConverter.GetBytes(value);
}
/**
*
*/
public static string ReadBytesToString(byte[] src, int Start, Encoding encoding = null)
{
List<byte> bytes = new List<byte>();
int index = 0;
while (true)
{
bytes.Add(src[Start + index]);
if (src[Start + index + 1] == 0x00)
break;
index++;
}
if (encoding == null)
encoding = Encoding.GetEncoding("Shift-JIS");
string str = encoding.GetString(bytes.ToArray());
return str;
}
/**
*
*/
public static string ReadBytesToString(byte[] src, Encoding encoding = null)
{
if (encoding == null)
encoding = Encoding.GetEncoding("Shift-JIS");
string str = encoding.GetString(src.ToArray());
return str;
}
/**
* int到byte[] byte高位在前
*/
public static void ModifyIntHexToBytes(byte[] srcdata, int targetvalue, int startoffset, int srclenght)
{
byte[] targetVal = intToBytes(targetvalue);
//抹去数据
for (int i = 0; i < srclenght; i++)
srcdata[startoffset + i] = 0x00;
for (int i = 0; i < targetVal.Length && i < srclenght; i++)
srcdata[startoffset + i] = targetVal[i];
}
/**
* byte[]byte[] byte高位在前
*/
public static void ModifyDataToBytes(byte[] srcdata, byte[] targetVal, int startoffset)
{
//抹去数据
for (int i = 0; i < targetVal.Length; i++)
srcdata[startoffset + i] = 0x00;
for (int i = 0; i < targetVal.Length && i < targetVal.Length; i++)
srcdata[startoffset + i] = targetVal[i];
}
/**
*
*/
public static bool CheckDataEquals(byte[] srcdata, byte[] targetVal, int startoffset)
{
byte[] temp = new byte[targetVal.Length];
for (int i = 0; i < targetVal.Length && i < targetVal.Length; i++)
temp[i] = srcdata[startoffset + i];
return Equals(targetVal, temp);
}
/// <summary>
/// 另一种16进制转10进制的处理方式Multiplier参与*16的循环很巧妙对Multiplier的处理很推荐逻辑统一
/// </summary>
/// <param name="HexaDecimalString"></param>
/// <returns></returns>
public static int HexaToDecimal(string HexaDecimalString)
{
int Decimal = 0;
int Multiplier = 1;
for (int i = HexaDecimalString.Length - 1; i >= 0; i--)
{
Decimal += HexaToDecimal(HexaDecimalString[i]) * Multiplier;
Multiplier *= 16;
}
return Decimal;
}
static int HexaToDecimal(char c)
{
switch (c)
{
case '0':
return 0;
case '1':
return 1;
case '2':
return 2;
case '3':
return 3;
case '4':
return 4;
case '5':
return 5;
case '6':
return 6;
case '7':
return 7;
case '8':
return 8;
case '9':
return 9;
case 'A':
case 'a':
return 10;
case 'B':
case 'b':
return 11;
case 'C':
case 'c':
return 12;
case 'D':
case 'd':
return 13;
case 'E':
case 'e':
return 14;
case 'F':
case 'f':
return 15;
}
return -1;
}
public static string UIntToHexStr(uint val)
{
byte[] Hexbytes = uintToBytes(val);
string HexKey = "";
//迎合地图表数据,还得高位在前
for (int j = 0; j < Hexbytes.Length && j < 2; j++)
{
string temp = Hexbytes[j].ToString("X");
if (temp.Length == 1)
{
temp = "0" + temp;
}
HexKey = temp + HexKey;
}
return HexKey;
}
}
}

18203
Helper/MHHelper.cs Normal file

File diff suppressed because it is too large Load Diff

161
Program.cs Normal file
View File

@ -0,0 +1,161 @@
using Axibug.MHFSaveAutoConverter.Data;
using Axibug.MHFSaveAutoConverter.SQL;
using HaoYue.MHFUserSrv.Server.Common;
using Npgsql;
using NpgsqlTypes;
using System.Text;
using static Axibug.MHFSaveAutoConverter.DataStruct.MHFSaveDataCfg;
namespace Axibug.MHFSaveAutoConverter
{
internal class Program
{
static void Main(string[] args)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Console.Title = "皓月云MHF存档迁移工具 ver 0.1.0";
Console.WriteLine("读取配置");
try
{
Config.LoadCfg("cfg.ini");
}
catch (Exception ex)
{
Console.WriteLine($"配置读取失败,{ex.ToString()}");
Console.ReadLine();
return;
}
Console.WriteLine("配置读取成功");
while (true)
{
Console.WriteLine($"==欢迎使用 {Console.Title} Axibug.MHFSaveAutoConverter==");
string[] verlist = Enum.GetNames(typeof(MHFVer));
MHFVer src;
MHFVer target;
while (true)
{
Console.WriteLine("Step1.选择要继承的角色的原始MHF版本: Select the original MHF version of the character you want to inherit:");
for (int i = 0; i < verlist.Length; i++)
Console.WriteLine($"[{i}]{verlist[i]}");
int srcidx;
string ver = Console.ReadLine();
if (!int.TryParse(ver, out srcidx))
continue;
if (srcidx >= 0 && srcidx < verlist.Length)
{
src = (MHFVer)Enum.Parse(typeof(MHFVer), verlist[srcidx]);
break;
}
}
while (true)
{
Console.WriteLine("Step2.请选择目标MHF版本: Please select the target MHF version:");
for (int i = 0; i < verlist.Length; i++)
Console.WriteLine($"[{i}]{verlist[i]}");
int idx;
string ver = Console.ReadLine();
if (!int.TryParse(ver, out idx))
continue;
if (idx >= 0 && idx < verlist.Length)
{
target = (MHFVer)Enum.Parse(typeof(MHFVer), verlist[idx]);
break;
}
}
Console.WriteLine($"step3.请输入[{src}]版本中的源Characters表中的角色ID: Please enter the character ID from the source Characters table in [{src}] version:");
if (!long.TryParse(Console.ReadLine(), out long cid))
{
Console.WriteLine("输入有误");
continue;
}
Console.WriteLine("===>操作的角色ID:" + cid);
if (!SaveDataCoverter.loadCharacterOLD(cid, out string name,out bool is_female, out byte[] srcdata))
{
Console.WriteLine("读取失败");
continue;
}
Console.WriteLine($"[{src}]角色{cid}:[{name}]数据加载完毕");
Console.WriteLine($"step4.是否升级存档到{target}(y),或仅Dump来自的{src}存档(n)");
Console.WriteLine($"step4.Do you want to upgrade the save file to {target}(y), or just dump the {src} save file(n)?");
bool bconvert = false;
string tempinput = Console.ReadLine().ToLower();
if (tempinput == "y")
{
bconvert = true;
}
else if (tempinput != "n")
{
Console.WriteLine("输入有误");
continue;
}
if (!SaveDataCoverter.ConvertSaveData(src, target, cid, name, !bconvert, srcdata, out byte[] targetdata, out string err))
{
Console.WriteLine($"处理失败:{err}");
continue;
}
if (bconvert == false)
continue;
Console.WriteLine($"step 5.[可选]将升级数据导入到目标数据库。若回车则取消。输入目标userid,则在目标版本数据库下Characters表插入数据据并关联您的userid");
Console.WriteLine($"Step 5. [Optional] Import the upgrade data into the target database. Press Enter to cancel. Enter the target userid, and the data will be inserted into the Characters table in the target version database, associated with your userid");
if (!long.TryParse(Console.ReadLine(), out long targetuserid))
{
Console.WriteLine("输入有误");
continue;
}
if (!InsertTargetDB(targetuserid, is_female, name, targetdata))
{
Console.WriteLine($"写入目标{target}数据库失败");
continue;
}
Console.WriteLine($"写入目标{target}数据库成功!");
Console.WriteLine("======继续?=====");
}
}
public static bool InsertTargetDB(long uid,bool is_female,string name, byte[] targetdata)
{
string str = "INSERT INTO \"public\".\"characters\" (\"user_id\", \"is_female\", \"is_new_character\", \"name\", \"unk_desc_string\", \"gr\", \"hr\", \"weapon_type\", \"last_login\", \"savedata\", \"decomyset\", \"hunternavi\", \"otomoairou\", \"partner\", \"platebox\", \"platedata\", \"platemyset\", \"rengokudata\", \"savemercenary\", \"restrict_guild_scout\", \"minidata\", \"gacha_items\", \"daily_time\", \"house_info\", \"login_boost\", \"skin_hist\", \"kouryou_point\", \"gcp\", \"guild_post_checked\", \"time_played\", \"weapon_id\", \"scenariodata\", \"savefavoritequest\", \"friends\", \"blocked\", \"deleted\", \"cafe_time\", \"netcafe_points\", \"boost_time\", \"cafe_reset\", \"bonus_quests\", \"daily_quests\", \"promo_points\", \"rasta_id\", \"pact_id\", \"stampcard\", \"mezfes\") " +
$"VALUES ({uid}, '{(is_female?"t":"f")}', 'f', '{name}', '', 0, 0, 0, 1750087006, @savedata, NULL, NULL, NULL, E'\\\\001cmp 20110113 \\\\000\\\\000\\\\002\\\\001\\\\250\\\\000\\\\377\\\\000\\\\255'::bytea, NULL, NULL, NULL, NULL, NULL, 'f', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, '2025-05-26 11:24:26.725902+08', 0, 0, E'\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000'::bytea, E'\\\\001\\\\000\\\\001\\\\000\\\\001\\\\000\\\\001\\\\000\\\\001\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000'::bytea, '', '', 'f', 0, 0, NULL, NULL, 0, 0, 0, NULL, NULL, 0, NULL);";
var savedataparam = new NpgsqlParameter("@savedata", NpgsqlDbType.Bytea);
savedataparam.Value = targetdata;
return SQLRUN_TARGET_DB.ExcuteSQL(str,new List<NpgsqlParameter> { savedataparam });
}
//public static SaveDataEntity SetData(long cid, string name, byte[] srcdata)
//{
// string path = $"savedata_cid_{cid}_{DateTime.Now.ToString("yyyyMMddHHmmss")}" + "_解密.bin";
// byte[] decdata = MHFCompression.Decompress(srcdata);
// SaveDataEntity se = new SaveDataEntity(MHFVer.FW5, MHFVer.GG, decdata);
// //SaveDataEntity se = new SaveDataEntity(MHFVer.GG, MHFVer.GG, decdata);
// System.IO.File.WriteAllBytes(path, decdata);
// Console.WriteLine($"角色数据{name},已保存数据到{path}");
// Console.WriteLine($"是否升级存档到MHFGG");
// if (Console.ReadLine().ToLower() == "y")
// {
// string updateoutpath = $"升级GG_savedata_cid_{cid}_{DateTime.Now.ToString("yyyyMMddHHmmss")}" + "_已修改.bin";
// byte[] updatedata = se.DoConvert();
// System.IO.File.WriteAllBytes(updateoutpath, updatedata);
// //Console.WriteLine($"是否验证数据是否正确?");
// //if (Console.ReadLine().ToLower() == "y")
// //{
// // new SaveDataEntity(MHFVer.GG, MHFVer.GG, updatedata);
// //}
// }
//}
}
}

125
SQL/SQLRUN_SRC_DB.cs Normal file
View File

@ -0,0 +1,125 @@
using HaoYue.MHFUserSrv.Server.Common;
using Npgsql;
using System.Data;
namespace Axibug.MHFSaveAutoConverter.SQL
{
internal static class SQLRUN_SRC_DB
{
static string connectionString => $"Host={Config.DictCfg["dbServer"]};Port={Config.DictCfg["dbPort"]};Database={Config.DictCfg["src_dbName"]};Username={Config.DictCfg["dbUser"]};Password={Config.DictCfg["dbPwd"]};";
static Queue<NpgsqlConnection> QueueCon = new Queue<NpgsqlConnection>();
static int PoolLimit = 2;
static int GetPoolCount()
{
return QueueCon.Count;
}
static NpgsqlConnection GetSqlConnect()
{
lock (QueueCon)
{
NpgsqlConnection con;
if (QueueCon.Count > 0)
{
con = QueueCon.Dequeue();
if (con.State < ConnectionState.Open)
{
con.Dispose();
NpgsqlConnection newcon = new NpgsqlConnection(connectionString);
newcon.Open();
con = newcon;
}
}
else
{
NpgsqlConnection newcon = new NpgsqlConnection(connectionString);
newcon.Open();
con = newcon;
}
return con;
}
}
static void PushConnect(NpgsqlConnection con)
{
lock (QueueCon)
{
if (QueueCon.Count < PoolLimit && con.State >= ConnectionState.Open)
{
QueueCon.Enqueue(con);
}
else
{
if (con.State >= ConnectionState.Open)
{
con.Close();
}
con.Dispose();
}
}
}
public static bool QuerySQL(string strSql, out DataTable dt)
{
dt = null;
if (string.IsNullOrEmpty(strSql))
return false;
NpgsqlConnection con = GetSqlConnect();
bool bneedPush = true;
try
{
NpgsqlDataAdapter sda = new NpgsqlDataAdapter(strSql, con);
DataSet ds = new DataSet();
sda.Fill(ds);
PushConnect(con);
bneedPush = false;
dt = ds.Tables[0];
return true;
}
catch
{
if (bneedPush)
{
PushConnect(con);
}
return false;
}
}
public static bool ExcuteSQL(string strSql)
{
NpgsqlConnection con = GetSqlConnect();
bool bneedPush = true;
if (string.IsNullOrEmpty(strSql))
return false;
string strSqlinsert = strSql;
int setnumbert = 0;
try
{
using (NpgsqlCommand SqlCommand = new NpgsqlCommand(strSqlinsert, con))
{
setnumbert = SqlCommand.ExecuteNonQuery();
}
PushConnect(con);
bneedPush = false;
return true;
}
catch (Exception ex)
{
if (bneedPush)
{
PushConnect(con);
}
return false;
}
}
public static DateTime ConvertTimestamptzToDatetime(object obj)
{
return TimeZoneInfo.ConvertTimeFromUtc((DateTime)obj, TimeZoneInfo.Local);
}
}
}

132
SQL/SQLRUN_TARGET_DB.cs Normal file
View File

@ -0,0 +1,132 @@
using HaoYue.MHFUserSrv.Server.Common;
using Npgsql;
using System.Data;
namespace Axibug.MHFSaveAutoConverter.SQL
{
internal static class SQLRUN_TARGET_DB
{
static string connectionString => $"Host={Config.DictCfg["dbServer"]};Port={Config.DictCfg["dbPort"]};Database={Config.DictCfg["target_dbName"]};Username={Config.DictCfg["dbUser"]};Password={Config.DictCfg["dbPwd"]};";
static Queue<NpgsqlConnection> QueueCon = new Queue<NpgsqlConnection>();
static int PoolLimit = 2;
static int GetPoolCount()
{
return QueueCon.Count;
}
static NpgsqlConnection GetSqlConnect()
{
lock (QueueCon)
{
NpgsqlConnection con;
if (QueueCon.Count > 0)
{
con = QueueCon.Dequeue();
if (con.State < ConnectionState.Open)
{
con.Dispose();
NpgsqlConnection newcon = new NpgsqlConnection(connectionString);
newcon.Open();
con = newcon;
}
}
else
{
NpgsqlConnection newcon = new NpgsqlConnection(connectionString);
newcon.Open();
con = newcon;
}
return con;
}
}
static void PushConnect(NpgsqlConnection con)
{
lock (QueueCon)
{
if (QueueCon.Count < PoolLimit && con.State >= ConnectionState.Open)
{
QueueCon.Enqueue(con);
}
else
{
if (con.State >= ConnectionState.Open)
{
con.Close();
}
con.Dispose();
}
}
}
public static bool QuerySQL(string strSql, out DataTable dt)
{
dt = null;
if (string.IsNullOrEmpty(strSql))
return false;
NpgsqlConnection con = GetSqlConnect();
bool bneedPush = true;
try
{
NpgsqlDataAdapter sda = new NpgsqlDataAdapter(strSql, con);
DataSet ds = new DataSet();
sda.Fill(ds);
PushConnect(con);
bneedPush = false;
dt = ds.Tables[0];
return true;
}
catch
{
if (bneedPush)
{
PushConnect(con);
}
return false;
}
}
public static bool ExcuteSQL(string strSql, List<NpgsqlParameter> param = null)
{
NpgsqlConnection con = GetSqlConnect();
bool bneedPush = true;
if (string.IsNullOrEmpty(strSql))
return false;
string strSqlinsert = strSql;
int setnumbert = 0;
try
{
using (NpgsqlCommand SqlCommand = new NpgsqlCommand(strSqlinsert, con))
{
if (param != null)
{
foreach (NpgsqlParameter p in param)
{
SqlCommand.Parameters.Add(p);
}
}
setnumbert = SqlCommand.ExecuteNonQuery();
}
PushConnect(con);
bneedPush = false;
return true;
}
catch (Exception ex)
{
if (bneedPush)
{
PushConnect(con);
}
return false;
}
}
public static DateTime ConvertTimestamptzToDatetime(object obj)
{
return TimeZoneInfo.ConvertTimeFromUtc((DateTime)obj, TimeZoneInfo.Local);
}
}
}

6
cfg.ini Normal file
View File

@ -0,0 +1,6 @@
dbServer=
dbPort=5432
src_dbName=
target_dbName=
dbUser=
dbPwd=