新增存档修复功能

This commit is contained in:
sin365 2025-09-19 18:25:00 +08:00
parent cd7399e2c0
commit a83c97166c
6 changed files with 445 additions and 36 deletions

View File

@ -64,6 +64,11 @@ namespace Axibug.MHFSaveAutoConverter.DataStruct
{
return this.ToString();
}
public virtual bool FixedData(out string log)
{
throw new NotImplementedException();
}
}
public class s_base_Byte : s_Base
@ -269,10 +274,10 @@ namespace Axibug.MHFSaveAutoConverter.DataStruct
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];
if (i == 0) itemiddata[0] = data[startptr + i];
if (i == 1) itemiddata[1] = data[startptr + i];
if (i == 2) countdata[0] = data[startptr + i];
if (i == 3) countdata[1] = data[startptr + i];
}
itemdata.Add((HexHelper.bytesToInt(itemiddata, 2, 0), HexHelper.bytesToInt(countdata, 2, 0)));
}
@ -280,7 +285,55 @@ namespace Axibug.MHFSaveAutoConverter.DataStruct
str += $"{MHHelper.Get2MHFItemName(item.Item1)}:{item.Item2}\r\n";
return str;
}
public override bool FixedData(out string log)
{
if (!SrcVerHad)
{
log = "没有数据";
return false;
}
log = this.GetType().Name + "\r\n";
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 == 0) itemiddata[0] = data[startptr + i];
if (i == 1) itemiddata[1] = data[startptr + i];
if (i == 2) countdata[0] = data[startptr + i];
if (i == 3) countdata[1] = data[startptr + i];
}
uint itemid = HexHelper.bytesToUInt(itemiddata, 2, 0);
uint count = HexHelper.bytesToUInt(countdata, 2, 0);
if (itemid < 0)
continue;
//log += $"[{block}]{itemid}:{MHHelper.Get2MHFItemName((int)itemid)}:{count}\r\n";
if (SrcVer == MHFVer.GG)
{
bool needfix = (itemid >= 9749);
if (needfix)
{
//抹除数据
for (int i = 0; i < SrcCfg.block_single_len; i++)
data[startptr + i] = 0x00;
log += $"抹除数据:[{block}]{itemid}:{MHHelper.Get2MHFItemName((int)itemid)}:{count}\r\n";
}
}
}
return false;
}
}
public class s_pPlaytime : s_4byte_UInt32_Base
{
public override string ToString()
@ -340,18 +393,18 @@ namespace Axibug.MHFSaveAutoConverter.DataStruct
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_片手_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 { }
@ -412,7 +465,8 @@ namespace Axibug.MHFSaveAutoConverter.DataStruct
public class s_pRP : s_2byte_Int16_Base { }
public class s_pKQF : s_base_Bytesarray { }
public class ExtraBox背包 : s_base_BytesarrayGroup { }
public class s_ItemPouch背包 : s_base_BytesarrayGroup {
public class s_ItemPouch背包 : s_base_BytesarrayGroup
{
public override string ToString()
{

View File

@ -0,0 +1,195 @@
using System;
using System.Collections.Generic;
using System.IO;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace MonsterHunterSaveBruteForce
{
class MH2DosSaveDataLoader
{
// 已知数值常量
private const ushort KnownMoney = 5929; // 0x1729
private const ushort KnownExp = 6525; // 0x197D
private const ushort ItemLargePotion = 0x0008; // 回復藥・大 的ID
private const ushort ItemLargePotionQty = 5; // 回復藥・大 的数量
// 道具箱特征前6个道具
private static readonly List<(ushort id, ushort quantity)> ItemPattern = new List<(ushort, ushort)>
{
(0x0001, 1), // 調合書①入門編
(0x0002, 1), // 調合書②初級編
(0x0003, 1), // 調合書③中級編
(0x0007, 3), // 回復藥
(ItemLargePotion, ItemLargePotionQty), // 回復藥・大
(0x0015, 2) // 冷飲
};
public static void LoadSaveData()
{
Console.WriteLine("===== PS2怪物猎人2存档暴力破解工具 =====");
Console.WriteLine("此工具使用金钱、经验和道具特征进行暴力破解");
// 1. 加载存档
byte[] saveData = File.ReadAllBytes("play00.bin");
Console.WriteLine($"存档大小: {saveData.Length} 字节");
// 2. 暴力破解
BruteForceDecryption(saveData);
}
static void BruteForceDecryption(byte[] data)
{
Console.WriteLine("开始暴力破解,这可能需要一些时间...");
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
int totalAttempts = 0;
const int reportInterval = 100000; // 每100,000次尝试报告一次进度
// 限制搜索范围提高效率
//int searchRange = Math.Min(0x10000, data.Length - 20);
int searchRange = data.Length - 20;
// 尝试所有可能的密钥组合 (0x0000 - 0xFFFF)
for (ushort key = 0; key < ushort.MaxValue; key++)
{
// 解密整个存档
byte[] decrypted = DecryptWithKey(data, key);
// 验证金钱和经验值
var (moneyFound, expFound) = ValidateMoneyAndExp(decrypted);
// 验证道具箱特征
//bool itemsValid = ValidateItemBox(decrypted);
bool itemsValid = true;
// 如果三项验证都通过
//if (moneyFound && expFound && itemsValid)
if(ValidateName(decrypted))
{
stopwatch.Stop();
Console.WriteLine($"\n暴力破解成功! 密钥: 0x{key:X4}");
Console.WriteLine($"耗时: {stopwatch.Elapsed.TotalSeconds:F2} 秒");
Console.WriteLine($"尝试次数: {totalAttempts}");
// 保存解密文件
//File.WriteAllBytes("decrypted_play00.bin", decrypted);
Console.WriteLine("解密文件已保存: decrypted_play00.bin");
return;
}
// 报告进度
if (++totalAttempts % reportInterval == 0)
{
Console.WriteLine($"已尝试 {totalAttempts} 种密钥组合... 当前密钥: 0x{key:X4}");
}
}
stopwatch.Stop();
Console.WriteLine($"\n暴力破解完成但未找到有效密钥。耗时: {stopwatch.Elapsed.TotalSeconds:F2} 秒");
Console.WriteLine($"总尝试次数: {totalAttempts}");
}
static byte[] DecryptWithKey(byte[] data, ushort key)
{
byte[] keyBytes = BitConverter.GetBytes(key);
byte[] decrypted = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
decrypted[i] = (byte)(data[i] ^ keyBytes[i % 2]);
}
return decrypted;
}
static (bool moneyFound, bool expFound) ValidateMoneyAndExp(byte[] decrypted)
{
bool moneyFound = false;
bool expFound = false;
// 扫描整个文件寻找金钱和经验值
for (int i = 0; i < decrypted.Length - 1; i++)
{
ushort value = BitConverter.ToUInt16(decrypted, i);
// 检查是否是金钱值
if (value == KnownMoney)
{
moneyFound = true;
}
// 检查是否是经验值
if (value == KnownExp)
{
expFound = true;
}
// 如果两个都已找到,提前退出
if (moneyFound && expFound)
{
break;
}
}
return (moneyFound, expFound);
}
static bool ValidateName(byte[] data)
{
byte[] checkdata = new byte[4] { 0xE1, 0xA9, 0x8C, 0x8E };
// 扫描整个文件寻找道具箱特征
for (int offset = 0; offset < data.Length - checkdata.Length; offset++)
{
for (int i = 0; i < checkdata.Length; i++)
{
if (data[0] == checkdata[0]
&& data[1] == checkdata[1]
&& data[2] == checkdata[2]
&& data[3] == checkdata[3])
return true;
}
}
return false;
}
static bool ValidateItemBox(byte[] decrypted)
{
// 尝试不同的道具结构 (4字节和8字节)
return ValidateItemStructure(decrypted, 4) || ValidateItemStructure(decrypted, 8);
}
static bool ValidateItemStructure(byte[] data, int slotSize)
{
// 扫描整个文件寻找道具箱特征
for (int offset = 0; offset < data.Length - (slotSize * 6); offset++)
{
bool patternMatch = true;
// 检查前6个道具是否匹配已知模式
for (int slot = 0; slot < 6; slot++)
{
int slotOffset = offset + (slot * slotSize);
// 获取道具ID和数量
ushort itemId = BitConverter.ToUInt16(data, slotOffset);
ushort quantity = BitConverter.ToUInt16(data, slotOffset + 2);
// 检查是否匹配预期值
if (itemId != ItemPattern[slot].id || quantity != ItemPattern[slot].quantity)
{
patternMatch = false;
break;
}
}
// 如果找到匹配模式
if (patternMatch)
{
return true;
}
}
return false;
}
}
}

View File

@ -1,5 +1,7 @@
using Axibug.MHFSaveAutoConverter.DataStruct;
using Axibug.MHFSaveAutoConverter.SQL;
using Npgsql;
using NpgsqlTypes;
using System.Data;
using System.Text;
using static Axibug.MHFSaveAutoConverter.DataStruct.MHFSaveDataCfg;
@ -8,6 +10,24 @@ namespace Axibug.MHFSaveAutoConverter.Data
{
public class SaveDataCoverter
{
public static bool InsertTargetDB_new(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 bool UpdateTargetDB_old(long cid, byte[] targetdata)
{
string str = $"UPDATE \"characters\" set savedata = @savedata where \"id\" = {cid};";
var savedataparam = new NpgsqlParameter("@savedata", NpgsqlDbType.Bytea);
savedataparam.Value = targetdata;
return SQLRUN_SRC_DB.ExcuteSQL(str, new List<NpgsqlParameter> { savedataparam });
}
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}";
@ -60,5 +80,25 @@ namespace Axibug.MHFSaveAutoConverter.Data
return false;
}
}
public static bool FixedSaveData(MHFVer from, MHFVer target, byte[] src, out byte[] targetdata, out string err)
{
try
{
byte[] decdata = MHFCompression.Decompress(src);
SaveDataEntity se = new SaveDataEntity(from, target, decdata);
targetdata = se.FixedEquipBox(decdata, out string log);
err = default;
Console.WriteLine(log);
return true;
}
catch (Exception ex)
{
err = ex.ToString();
targetdata = default;
return false;
}
}
}
}

View File

@ -2,6 +2,7 @@
using System.Data;
using static Axibug.MHFSaveAutoConverter.DataStruct.DataStruct;
using static Axibug.MHFSaveAutoConverter.DataStruct.MHFSaveDataCfg;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Axibug.MHFSaveAutoConverter.DataStruct
{
public class SaveDataEntity
@ -70,6 +71,35 @@ namespace Axibug.MHFSaveAutoConverter.DataStruct
Console.WriteLine("====写入完毕====");
return data;
}
public byte[] FixedEquipBox(byte[] srcdata, out string log)
{
log = null;
byte[] targetdata = srcdata.ToArray();
Console.WriteLine("====尝试开始写入====");
foreach (var singledata in saveHandles)
{
if (!(singledata is s_Itembox itembox))
continue;
itembox.FixedData(out log);
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, targetdata);
Console.WriteLine($"写入:{singledata.GetType().Name} =>{(ret ? "" : "")}");
Console.WriteLine(str);
}
Console.WriteLine("====写入完毕====");
return targetdata;
}
}
}

View File

@ -1,8 +1,12 @@
using Axibug.MHFSaveAutoConverter.Data;
using Axibug.MHFSaveAutoConverter.DataStruct;
using Axibug.MHFSaveAutoConverter.SQL;
using HaoYue.MHFUserSrv.Server.Common;
using MonsterHunterSaveBruteForce;
using Npgsql;
using NpgsqlTypes;
using System;
using System.Runtime.ConstrainedExecution;
using System.Text;
using static Axibug.MHFSaveAutoConverter.DataStruct.MHFSaveDataCfg;
@ -13,6 +17,7 @@ namespace Axibug.MHFSaveAutoConverter
static void Main(string[] args)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
//MH2DosSaveDataLoader.LoadSaveData();
Console.Title = "皓月云MHF存档迁移工具 ver 0.1.0";
Console.WriteLine("读取配置");
try
@ -29,6 +34,24 @@ namespace Axibug.MHFSaveAutoConverter
while (true)
{
Console.WriteLine($"==欢迎使用 {Console.Title} Axibug.MHFSaveAutoConverter==");
int funcid = 0;
while (true)
{
Console.WriteLine("Step0.使用什么功能?[0]存档继承,[1]存档修复 What function to use? [0] userdata inheritance, [1] userdata repair");
string var = Console.ReadLine();
if (!int.TryParse(var, out funcid))
continue;
break;
}
if (funcid == 1)
{
FixedItemBox();
break;
}
string[] verlist = Enum.GetNames(typeof(MHFVer));
MHFVer src;
@ -73,7 +96,7 @@ namespace Axibug.MHFSaveAutoConverter
continue;
}
Console.WriteLine("===>操作的角色ID:" + cid);
if (!SaveDataCoverter.loadCharacterOLD(cid, out string name,out bool is_female, out byte[] srcdata))
if (!SaveDataCoverter.loadCharacterOLD(cid, out string name, out bool is_female, out byte[] srcdata))
{
Console.WriteLine("读取失败");
continue;
@ -112,7 +135,7 @@ namespace Axibug.MHFSaveAutoConverter
}
//if (!UpdateTargetDB(targetuserid, is_female, name, targetdata))
if (!InsertTargetDB(targetuserid, is_female, name, targetdata))
if (!SaveDataCoverter.InsertTargetDB_new(targetuserid, is_female, name, targetdata))
{
Console.WriteLine($"写入目标{target}数据库失败");
continue;
@ -124,22 +147,54 @@ namespace Axibug.MHFSaveAutoConverter
}
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 bool UpdateTargetDB(long cid, bool is_female, string name, byte[] targetdata)
public static bool FixedItemBox()
{
string str = $"UPDATE \"characters\" set savedata = @savedata where \"id\" = {cid};";
var savedataparam = new NpgsqlParameter("@savedata", NpgsqlDbType.Bytea);
savedataparam.Value = targetdata;
return SQLRUN_TARGET_DB.ExcuteSQL(str, new List<NpgsqlParameter> { savedataparam });
string[] verlist = Enum.GetNames(typeof(MHFVer));
MHFVer src;
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;
}
}
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("输入有误");
return false;
}
Console.WriteLine("===>操作的角色ID:" + cid);
if (!SaveDataCoverter.loadCharacterOLD(cid, out string name, out bool is_female, out byte[] srcdata))
{
Console.WriteLine("读取失败");
return false;
}
if (!SaveDataCoverter.FixedSaveData(src, src, srcdata, out byte[] targetdata, out string err))
{
Console.WriteLine($"处理失败:{err}");
return false;
}
if (!SaveDataCoverter.UpdateTargetDB_old(cid, targetdata))
{
Console.WriteLine($"处理失败:{err}");
return false;
}
Console.WriteLine($"写入玩家{name}修正数据");
return true;
}
//public static SaveDataEntity SetData(long cid, string name, byte[] srcdata)

View File

@ -88,7 +88,8 @@ namespace Axibug.MHFSaveAutoConverter.SQL
}
}
public static bool ExcuteSQL(string strSql)
public static bool ExcuteSQL(string strSql, List<NpgsqlParameter> param = null)
{
NpgsqlConnection con = GetSqlConnect();
bool bneedPush = true;
@ -100,6 +101,13 @@ namespace Axibug.MHFSaveAutoConverter.SQL
{
using (NpgsqlCommand SqlCommand = new NpgsqlCommand(strSqlinsert, con))
{
if (param != null)
{
foreach (NpgsqlParameter p in param)
{
SqlCommand.Parameters.Add(p);
}
}
setnumbert = SqlCommand.ExecuteNonQuery();
}
PushConnect(con);
@ -115,6 +123,33 @@ namespace Axibug.MHFSaveAutoConverter.SQL
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)
{