首页
社区
课程
招聘
[原创]某游戏的汉化实战
发表于: 2009-5-22 00:12 12885

[原创]某游戏的汉化实战

2009-5-22 00:12
12885

前一段时间写了两篇关于游戏封包格式反汇编的文章(虽然有一篇并没有分析出结果OTL),而且分析完了也没写出能提取资源的程序。于是这次我就由解包到翻译到封包到修改程序完整地写一篇教程。这次的游戏选择的是DC2PC,大小是7.62GB(- -|||)

一、资源分析
    一般对于汉化游戏而言,脚本是汉化者最重要的部分(大多数时候还要提取出部分图片进行修改,部分有过场动画的游戏还要提取出动画嵌入字幕再封回去,至于声音的话……难道有人打算将日语的配音换成普通话么)。一般脚本文件要么是多个大小不超过1MB的文件放在特定的文件夹,要么就是单个大小不超过20M的单个文件,还有种情况就是多种资源封装在一起组成一个很大的资源文件。对于本例的游戏而言,脚本文件放在\DC2PC\Advdata\MES\目录下面,有很多个大小在20KB以内的文件。
    至于图片等资源文件,为求简洁,这里就不分析了。
    进入\DC2PC\Advdata\MES\下面,用WinHex随便打开一个文件,看到如下图所示:

    可以看出脚本文件里面能看到明文的要载入的资源位置,而后面有一段乱码,在日文编码中,一般日文的高位是0x80,可见,这段乱码是经过加密的。由此猜测,这段就是游戏中的对白。至于具体的加密方式……好吧,CIRCUS的游戏加密的方式一般都是每字节减0x20,这个也不例外。但本着学术研究的精神,为严谨起见,下面我还是会用OD进行调试的。
    在这个文件夹发现有个start.mes的文件,打开看看发现没有像前面那样的乱码。如无意外,这个应该是控制游戏启动的脚本。既然这样,我们就应该在开始游戏的时候来进行分析了。

二、提取文本
    到了这一步,就轮到od登场了,首先载入游戏,然后F9运行,然后在游戏界面点下开始游戏的用时在od的命令行输入bp CreateFileA,接着od就将打开文件的操作断了下来。一直按F9到打开的是mes后缀名的文件为止。而目前要打开的是fst_1216_a1_cmn.mes这个文件。接着对ReadFile下断,断下后单步运行到函数返回,回到下面的地方:

#include<windows.h>
#include<iostream>
#include<string.h>
using namespace std;

int main()
{
	HANDLE hfile=CreateFile

("c:\\fst_1216_a1_cmn.mes",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL);
	if(hfile==INVALID_HANDLE_VALUE)
	{
		cout<<"Can not open file!"<<endl;
		return 0;
	}
	DWORD size=GetFileSize(hfile,NULL);
	DWORD len;
	char *buff=new char [size];
	if(!ReadFile(hfile,(LPVOID)buff,size,&len,NULL))
	{
		cout<<"Can not read file!"<<endl;
		CloseHandle(hfile);
		return 0;
	}
	CloseHandle(hfile);
	char temp[128]={0};
	int j=0;
	hfile=CreateFile("d:\\a1_cmn.txt",GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL);
	if(hfile==INVALID_HANDLE_VALUE)
	{
		cout<<"Can not create file!"<<endl;
		return 0;
	}
	for(int i=0;i<size;i++)
	{
		j=0;
		if(buff[i]==0x4c)
		{
			while(buff[i+j])buff[i+j++]+=0x20;
			memcpy(temp,&buff[i],j);
			i+=j;
			temp[j]='\r';
			temp[++j]='\n';
			WriteFile(hfile,(LPVOID)temp,j,&len,NULL);
		}
	}
	delete [] buff;
	delete [] temp;

	CloseHandle(hfile);
	return 0;
}
#include<windows.h>
#include<iostream>
#include<string.h>
using namespace std;

int main()
{
	HANDLE hfile=CreateFile("c:\\fst_1216_a1_cmn.mes",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL);
	if(hfile==INVALID_HANDLE_VALUE)
	{
		cout<<"Can not open file!"<<endl;
		return 0;
	}
	DWORD size=GetFileSize(hfile,NULL);
	DWORD len;
	BYTE *buff=new BYTE [size];
	if(!ReadFile(hfile,(LPVOID)buff,size,&len,NULL))
	{
		cout<<"Can not read file!"<<endl;
		CloseHandle(hfile);
		return 0;
	}
	CloseHandle(hfile);
	int j=0,nu=0;
	hfile=CreateFile("d:\\a1_cmn.txt",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL);
	if(hfile==INVALID_HANDLE_VALUE)
	{
		cout<<"Can not create file!"<<endl;
		return 0;
	}
	DWORD textsize=GetFileSize(hfile,NULL);
	BYTE *temp=new BYTE [textsize];

	if(!ReadFile(hfile,(LPVOID)temp,textsize,&len,NULL))
	{
		cout<<"Can not read file!"<<endl;
		CloseHandle(hfile);
		return 0;
	}
	CloseHandle(hfile);
	hfile=CreateFile("d:\\fst_1216_a1_cmn.mes",GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL);
	if(hfile==INVALID_HANDLE_VALUE)
	{
		cout<<"Can not create new script file!"<<endl;
		return 0;
	}	
	for(int i=0;i<size;i++)
	{
		if(buff[i]>=0x4a&&buff[i]<0x4e)
		{
			while(buff[i++]){};
			while(temp[j]!=0x0d)
			{
				temp[j]-=0x20;
				WriteFile(hfile,(LPVOID)&temp[j++],1,&len,NULL);
			}
			WriteFile(hfile,(LPVOID)&nu,1,&len,NULL);
			WriteFile(hfile,(LPVOID)&buff[i],1,&len,NULL);
			j++;
		}
		else
		{
			WriteFile(hfile,(LPVOID)&buff[i],1,&len,NULL);
		}
	}
	delete [] temp;
	delete [] buff;
	SetFilePointer(hfile,0,NULL,FILE_BEGIN);
	size=GetFileSize(hfile,NULL);
	buff=new BYTE [size];
	if(!ReadFile(hfile,(LPVOID)buff,size,&len,NULL))
	{
		cout<<"Can not read file!"<<endl;
		CloseHandle(hfile);
		return 0;
	}
	DWORD *offsetarray=new DWORD [*(DWORD*)&buff[0]];
	DWORD sizeofheader=(*(DWORD*)&buff[0])*4+4;
	for(j=0,i=0;i<size;i++)
	{
		if(buff[i]>=0x4a&&buff[i]<0x4e)
		{
			offsetarray[j]=i-sizeofheader;
			while(buff[i++]);
			j++;
		}
	}
	SetFilePointer(hfile,4,NULL,FILE_BEGIN);
	WriteFile(hfile,(LPVOID)offsetarray,sizeofheader-4,&len,NULL);
	delete [] offsetarray;
	CloseHandle(hfile);
	return 0;
}

0041A0BF |> \6A 04 push 4 ; 读取四个字节
0041A0C1 |. 68 4CA0AF00 push 00AFA04C ; 缓冲区
0041A0C6 |. 56 push esi
0041A0C7 |. E8 64080100 call 0042A930 ; 读取文件
0041A0CC |. 8B15 4CA0AF00 mov edx, dword ptr [AFA04C]
0041A0D2 |. 8D0495 000000>lea eax, dword ptr [edx*4] ; 文件首双字*4
0041A0D9 |. 50 push eax ; 读取字节数
0041A0DA |. 68 2431B000 push 00B03124 ; 缓冲区
0041A0DF |. 56 push esi ; 未知
0041A0E0 |. E8 4B080100 call 0042A930 ; 读取文件
0041A0E5 |. 68 A0860100 push 186A0 ; 读取字节数
0041A0EA |. 68 B8D3AA00 push 00AAD3B8 ; 缓冲区
0041A0EF |. 56 push esi
0041A0F0 |. E8 3B080100 call 0042A930 ; 读取文件

0041C575 |. 892D 30CEB000 |mov dword ptr [B0CE30], ebp
0041C57B |. 0FBEB0 B8D3AA>|movsx esi, byte ptr [eax+AAD3B8]
0041C582 |. 83FE 29 |cmp esi, 29
0041C585 |. 897424 28 |mov dword ptr [esp+28], esi
0041C589 |. 8915 2CCEB000 |mov dword ptr [B0CE2C], edx
0041C58F |. 7D 32 |jge short 0041C5C3
0041C591 |. 8A90 B9D3AA00 |mov dl, byte ptr [eax+AAD3B9]

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (18)
雪    币: 2105
活跃值: (424)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
文章不错,收藏
2009-5-22 01:09
0
雪    币: 98803
活跃值: (201054)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
3
Support.
2009-5-22 01:39
0
雪    币: 2242
活跃值: (488)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
4
不错不错 学习了
2009-5-22 02:55
0
雪    币: 50161
活跃值: (20660)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
5
文章写的不错,欢迎这类文章。

将文章中的图片移到论坛本地来了,这样避免时间长了,原来链接有失效的可能。
2009-5-22 09:04
0
雪    币: 348
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
support 学习下思路。
楼主好基础
2009-5-22 10:27
0
雪    币: 115
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
赞一个,写的很不错啊
2009-5-22 11:10
0
雪    币: 716
活跃值: (162)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
8
文章很不错,学习
2009-5-22 11:11
0
雪    币: 70
活跃值: (74)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
没搞过这类东东,收藏起来好好学习
2009-5-22 11:16
0
雪    币: 233
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
汉化很辛苦的,学习
2009-5-22 11:25
0
雪    币: 370
活跃值: (52)
能力值: ( LV13,RANK:350 )
在线值:
发帖
回帖
粉丝
11
谢谢,学习了先
2009-5-22 11:26
0
雪    币: 1505
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
12
支持 学习  
2009-5-22 11:32
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
谢谢楼主分享经验
2009-5-22 12:04
0
雪    币: 445
活跃值: (35)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
14
感谢看雪老大的帮忙
2009-5-22 12:46
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
汉化很枯燥,你写的很精彩
2009-5-22 15:33
0
雪    币: 14
活跃值: (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
汉化辛苦,能坚持就是胜利。
2009-5-22 15:51
0
雪    币: 38
活跃值: (551)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
17
不错哦
搬个板凳严重学习!
2009-5-22 19:41
0
雪    币: 1718
活跃值: (5400)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
18
不错哦
搬个板凳严重学习!
2009-5-22 22:10
0
雪    币: 291
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
确实不错,收藏了
2009-5-26 13:51
0
游客
登录 | 注册 方可回帖
返回
//