免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
如果看不懂、不知道现在做的什么,那就跟着做完看效果,代码看不懂是正常的,只要会抄就行,抄着抄着就能懂了
内容参考于:易道云信息技术研究院
上一个内容:91.加载游戏语言文件到内存
码云版本号:aafa72010a0c9ffb42274c1c2ff3a61e627eb49f
代码下载地址,在 titan 目录下,文件名为:titan-利用哈希表实现快速读取文本内容.zip
链接:https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg
提取码:q9n5
--来自百度网盘超级会员V4的分享
HOOK引擎,文件名为:黑兔sdk升级版.zip
链接:https://pan.baidu.com/s/1IB-Zs6hi3yU8LC2f-8hIEw
提取码:78h8
--来自百度网盘超级会员V4的分享
以 91.加载游戏语言文件到内存 它的代码为基础进行修改
现在已经把游戏里的语言文件写到了本次磁盘里,并且把这个文件加载到了我们程序的内存中
找有冲突的中文
下图红框里是 textDatas 变量的地址,也就是从磁盘文件中加载的语言包的地址
然后打开x96dbg,然后附加到游戏
鼠标单击下图红框位置,也就是x96dbg的内存窗口位置
然后ctrl+g,然后输入,textDatas 变量的地址
如下图,在7CCFF27C位置鼠标右击选择在内存窗口中转到知道DWORD
然后就来到了语言包的内存区域,现在还没有赋值所以全是0
然后回到游戏点击确定,如下图红框
点击完确定就会有值了
这个原因是,弹框的代码是在解析语言包之前
然后第一个不是-1,它就是存在哈希冲突
然后找它的链表,上图是链表第一个数据,通过下图得出的589A8740是链表的第二个数据,现在要找链表的最后一个数据,找最后一个数据的愿意是为了测试我们通过磁盘文件加载的语言包是否可以用,然后DAC*30,这个30是十六进制的,30的十进制是48,这个48是 TextTable 结构的大小
TextTable 结构的大小也就是下图红框位置
然后鼠标单击x96dbg的内存窗口,然后按ctrl+g,然后输入地址,然后点确定转到
然后发现它还不是-1,所以它不是最后一个数据
继续找
看到它还不是-1
继续
然后就找到-1了
它的中文是,水系咏唱法术
0x58D6F9D0位置字符串的效果图
然后发现个事情0x7CCFF27C这个位置的值是0x5897B020,但是x96dbg跳过去之后不是了,如下图,是刚开始的地方,x96dbg给我们跳到了0x5897B050位置了,但是找哈希冲突的值也还是上方那样的找发,图都截完了才发现x96dbg给跳错了
TextManger.cpp文件的修改:修改了 LoadTextData函数、ReadTextById函数
#include "pch.h"
#include "TextManger.h"
unsigned TextManger::ReadTextProc = 0x10295FB0;
const wchar_t TextManger::NullText[2]{};
bool TextManger::CreateTextDat(const char* _filename)
{
if (!textTable) {
return false;
}
std::ofstream out(_filename, std::ios::out|std::ios::binary);
if (out.bad())return false;
out.write((char*)&hashCount, sizeof(hashCount));
for (int i = 0; i < hashCount; i++) {
// 游戏中的中文表结构是 中文名英文id 这样的
int lenId = strlen(textTable[i]->TxtId) + 1;
// 宽字节一个字是2字节,wcslen函数返回值是字的个数
int lenTxt = wcslen(textTable[i]->Txt) + 1;
lenTxt = lenTxt * 2;
out.write(textTable[i]->TxtId, lenId);
out.write((char*)textTable[i]->Txt, lenTxt);
}
out.close();
return true;
}
bool TextManger::LoadTextData(const char* _filename)
{
CString txt;
if (textDatas)return false;
std::ifstream file(_filename, std::ios::in | std::ios::binary|std::ios::ate);
if(file.bad()) {
return false;
}
unsigned buffSize = file.tellg();
TextBuff = new char[buffSize];
file.seekg(std::ios::beg);
file.read(TextBuff, buffSize);
// textTable = new PTextTable[Count[0]];
textDatas = new TextTable[Count[0]];
txt.Format(L"%X", &textDatas);
AfxMessageBox(txt);
char* buffStart = TextBuff + 4;
char* buffEnd = buffStart + buffSize;
int icount = 0;
while(buffStart < buffEnd){// 读取语言文件的数据到 textDatas 变量里
int lenId = strlen(buffStart) + 1;
memcpy(textDatas[icount].TxtId, buffStart, 0x24);
buffStart = buffStart + lenId;
int lenTxt = wcslen((wchar_t*)buffStart) + 1;
lenTxt = lenTxt * 2;
textDatas[icount].Txt = (wchar_t*)buffStart;
buffStart = buffStart + lenTxt;
icount++;
}
hashCount = Count[0];
HashIndexTable = new unsigned[hashCount];
memset(HashIndexTable, 0xFF, hashCount * sizeof(unsigned));
for (int i = 0; i < Count[0]; i++) {// 给 textDatas 变量添加哈希算法索引(哈希表存放 textDatas的下标)
unsigned hash = Hashcode(textDatas[i].TxtId);
hash = hash % (hashCount + 1);
if (HashIndexTable[hash] == -1) {
HashIndexTable[hash] = i;
}
else {
PTextTable nullTable = GetNullText(&textDatas[HashIndexTable[hash]]);
nullTable->next = i;
}
}
return false;
}
const wchar_t* TextManger::ReadTextById(const char* _id)
{
// unsigned callProc = 0x10295FB0;
if (textTable) {
int index;
_asm {
push ebx
mov ebx, this
push _id
call ReadTextProc
mov index, eax
pop ebx
}
if (index < 0)return NullText;
PTextTable* _table = textTable;
return (wchar_t*)_table[index]->Txt;
}
else {
unsigned hash = Hashcode(_id);
hash = hash % (hashCount + 1);
unsigned index = HashIndexTable[hash];
if (index > hashCount) {// index的值如果是-1那么它肯定比hashCount大
return NullText;
}
while (strcmp(textDatas[index].TxtId, _id)) {
index = textDatas[index].next;
if (index > hashCount)return NullText;
}
return textDatas[index].Txt;
}
}
unsigned TextManger::Hashcode(const char* id)
{
unsigned hash = 0;
for (int i = 0; id[i]; i++)
{
hash = hash * 131 + id[i];
}
return hash;
}
PTextTable TextManger::GetNullText(PTextTable _table)
{
while (_table->next != -1) {// 从链表中找出最后一个(next的值是-1的)
_table = &textDatas[_table->next];
}
return _table;
}
TextManger.h文件的修改:修改了 TextTable结构体
#pragma once
#include <iostream>
#include <fstream>
/*
03E1B73B | 68 58120304 | push fxgamelogic.4031258 | 4031258:"gui"
03E1B740 | E8 7BEF0F00 | call fxgamelogic.3F1A6C0 |
03E1B745 | 83C4 04 | add esp,4 |
03E1B748 | 85C0 | test eax,eax |
03E1B74A | 0F84 10090000 | je fxgamelogic.3E1C060 |
03E1B750 | 68 4C120304 | push fxgamelogic.403124C | 403124C:"TextManager"
03E1B755 | 50 | push eax |
03E1B756 | 8D4C24 64 | lea ecx,dword ptr ss:[esp+64] |
03E1B75A | 51 | push ecx |
03E1B75B | E8 00091000 | call fxgamelogic.3F1C060 |
03E1B760 | 8D5424 68 | lea edx,dword ptr ss:[esp+68] |
03E1B764 | 52 | push edx |
03E1B765 | E8 16EF0F00 | call fxgamelogic.3F1A680 | 111111111111
03E1B76A | 83C4 10 | add esp,10 |
03E1B76D | 85C0 | test eax,eax |
03E1B76F | 894424 14 | mov dword ptr ss:[esp+14],eax | [esp+14]:CD3DDDIDX10_DrawIndexedPrimitive+113
*/
// 游戏中的中文表结构是 中文名英文id 这样的
typedef class TextTable {
public:
int next = ((int)0xFFFFFFFF);
int un = sizeof(TextTable);
wchar_t* Txt;// 中文
char TxtId[0x24];// 中文对应的英文id
}*PTextTable;
typedef class TextManger
{
public:
static unsigned ReadTextProc;
static const wchar_t NullText[2];
private:
//int un[0x20];// 0x20 * 0x4 = 0x80
int un[0x1E];
union {
char* TextBuff;
unsigned* Count;
};
PTextTable textDatas = nullptr; // 中文结构
public:
PTextTable* textTable = nullptr; // 暂时无用
private:
int un1;
public:
unsigned hashCount;
unsigned* HashIndexTable = nullptr; // 哈希表
public:
bool CreateTextDat(const char* filename); // 导出语言包
bool LoadTextData(const char* _filename); // 加载 CreateTextDat函数写出的文件
const wchar_t* ReadTextById(const char* _id); // 根据id获取中文名
unsigned Hashcode(const char* id);// 哈希函数
// 获取一个未使用的中文与对应id结构,用与存放从文件中读取的中文与对应id
PTextTable GetNullText(PTextTable _table);
}*PTextManger;
CUIWnd_0.cpp文件的修改:修改了 OnBnClickedButton2函数
// CUIWnd_0.cpp: 实现文件
//
#include "pch.h"
#include "htdMfcDll.h"
#include "CUIWnd_0.h"
#include "afxdialogex.h"
#include "extern_all.h"
#include "GameOBJECTDef.h"
// CUIWnd_0 对话框
float _xNext = 0.0f;
float _yNext = 0.0f;
void _stdcall loops(HWND, UINT, UINT_PTR, DWORD) {
PAIM aim = Client->GetAimByName(L"r");
if (aim == nullptr) {
return;
}
//AfxMessageBox(aim->Name);
float xDis = fabs(Client->Player.x - aim->x);
float hDis = fabs(Client->Player.h - aim->h);
float yDis = fabs(Client->Player.y - aim->y);
float xNext, yNext, thisx, thisy;
int v[2]{ -1, 1 };
if (xDis > 12) {
// 强制修改角色x坐标的值
thisx = Client->Player.x;
Client->Player.x = Client->Player.x + 6 * v[Client->Player.x < aim->x];
xNext = Client->Player.x;
}
else {
xNext = aim->x;
}
if (yDis > 12) {
// 强制修改角色y坐标的值
thisy = Client->Player.y;
Client->Player.y = Client->Player.y + 6 * v[Client->Player.y < aim->y];
yNext = Client->Player.y;
}else{
yNext = aim->y;
}
if ((xDis < 2)&&(hDis < 2)&&(yDis < 2)) {
Client->MoveStop(aim->x, aim->h, aim->y, 0.0f);
}
else {
Client->MoveWalk(Client->Player.x,aim->h, Client->Player.x, 0.0f, 0.0f, xNext, yNext);
}
}
IMPLEMENT_DYNAMIC(CUIWnd_0, CDialogEx)
CUIWnd_0::CUIWnd_0(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_PAGE_0, pParent)
{
}
CUIWnd_0::~CUIWnd_0()
{
}
void CUIWnd_0::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CUIWnd_0, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON1, &CUIWnd_0::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON2, &CUIWnd_0::OnBnClickedButton2)
END_MESSAGE_MAP()
// CUIWnd_0 消息处理程序
void CUIWnd_0::OnBnClickedButton1()
{
// 发送坠落数据包
//Client->Fall();
设置移动速度
float Speed = 30.0;
Client->SetProperty(Client->Player.lId, INDEX_MoveSpeed, &Speed);
/*
// 修改血量
int HP = 10000000;
Client->SetProperty(Client->Player.lId, INDEX_HP, &HP);*/
/*CString txt;
txt.Format(L"通过AIM类获取角色信息 名字:%s,x坐标:%f", Client->Player.Name, Client->Player.x);
AfxMessageBox(txt);*/
// Client->HeartBeep();
// Client->TalkTo(L"r", L"打架吗?");
// Client->Talk(L"[欢迎来到麟科思]");
// Client->SelectRole(L"今晚打老虎");
/*Client->CreateRole(L"am4", 1.0, 2.0, 4.0, 8.0, "gui\BG_team\TeamRole\Teamrole_zq_humF_001.PNG",
"Face,0;Hat,0;Eyes,0;Beard,0;Ears,0;Tail,0;Finger,0;Cloth,0;Pants,0;Gloves,0;Shoes,0;Trait,0;HairColor,0;SkinColor,0;SkinMtl,0;Tattoo,0;TattooColor,16777215;",
"", 0.0);*/
//Client->SelectCamp("xuanrenZQ");
//Client->StartCreateRole();
//Client->DelRole(L"ranzhi11111");
/*
char buff[] = {
0xA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x4, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 00 ,0x00,
0x00, 0x07, 0x0E, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x31, 0x00, 0x32 ,0x00,
0x33, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
WinSock->OnSend(buff, sizeof(buff));
*/
/*char buff[] = {
0x27, 0x46, 0x92, 0x02, 0x00, 0x00, 0x89, 0x02, 0x00, 0x00, 0x06, 0x00, 0x06, 0x05,
0x00, 0x00, 0x00, 0x63, 0x68, 0x61, 0x74, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x07,
0x0A, 0x00, 0x00, 0x00, 0x34, 0x00, 0x33, 0x00, 0x39, 0x00, 0x39, 0x00, 0x00, 0x00,
0x07, 0x5A, 0x02, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32,
0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32,
0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32,
0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32,
0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32,
0x00, 0x32, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36,
0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36,
0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36,
0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x37,
0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00,
0x38, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00
};
WinSock->OnRecv(buff, sizeof(buff));*/
}
TextManger txtMangerMain;
void CUIWnd_0::OnBnClickedButton2()
{
// 根据语言表获取文字
//CString txt = txtManger->ReadTextById("npc_GRZJ_0001");// PGameProc->GetTextName("npc_GRZJ_0001");
//AfxMessageBox(txt);
//txtManger->CreateTextDat("F:\\语言包.txt");
// 读取导出到本地的语言包
txtMangerMain.LoadTextData("F:\\语言包.txt");
// CString txt = txtMangerMain.ReadTextById("npc_GRZJ_0001");// npc_GRZJ_0001没有哈希冲突
/*
Hui_event_3_1有哈希冲突,通过打印 textDatas 变量的内存地址
去x96db找的(第一个就冲突了,ui_event_3_1是第一个哈希表里的链表的最后一个)
如果第一个没有冲突,那就找一个 next 不是 -1 的,不是-1说明是链表,然后找链表最后一个就可以了
*/
CString txt = txtMangerMain.ReadTextById("ui_event_3_1");
AfxMessageBox(txt);
txt = txtMangerMain.ReadTextById("desc_Item_A550001_01_2");
AfxMessageBox(txt);
// 瞬移
//float decX = 3843.776123f;
//float decH = 11.731983f;
//float decY = -2005.533813f;
//float face = 0.0f;
//Client->Teleport(decX, decH, decY, face);
//PAIM aim = Client->GetAimByName(L"r");
//if (aim == nullptr) {
// return;
//}
//Client->Teleport(aim->x, aim->h, aim->y, aim->face);
// 修正坐标
/*PAIM aim = Client->GetAimByName(L"r");
if (aim == nullptr) {
return;
}
Client->SetCoord(Client->Player.lId, aim->x, aim->h, aim->y, aim->face);*/
// 面向
// Client->FaceTo(L"r");
// 飞天
// Client->HideMode = true;
// 遁地
//Client->MoveStop(Client->Player.x, Client->Player.h - 5, Client->Player.y, 0.0f);
// 跟随
// ::SetTimer(m_hWnd, 0x1000, 2000, loops);
//
// Client->Backtoroles();
}