• Unfortunately, we have experienced significant hard drive damage that requires urgent maintenance and rebuilding. The forum will be a state of read only until we install our new drives and rebuild all the configurations needed. Please follow our Facebook page for updates, we will be back up shortly! (The forum could go offline at any given time due to the nature of the failed drives whilst awaiting the upgrades.) When you see an Incapsula error, you know we are in the process of migration.

Extract Strings - Aid to Translation

Custom Title Activated
Loyal Member
Joined
May 26, 2007
Messages
5,545
Reaction score
1,315
[SIZE=+2]Release - ExtractStrings
Aid to Translation
[/SIZE]​
[SIZE=+1]Contents[/SIZE]
Introduction
Usage
Downloads
Credits​
[SIZE=+1]Introduction[/SIZE]
ExtractStrings.exe was initially developed to aid in the construction of .lng files for use in my International.dll libraries. It's output was almost a perfect match for a .lng file, but it does output information which is not string data... so the files still need a little cleaning up.

Osirus came to me asking about translating a newer client in a traditional way (a task he has done manually many times before) and I suggested he use ExtractStrings to tell him where particular Korean strings are in the executable. Between us, we made some changes that make traditional translation much more simple, and since then, I've cleaned the code up a little to make ExtractStrings easier to use.

There are lots of programs available which claim to "extract string information" from executables, and you can Google them. The trouble is, just about all of them look for Western European ANSI strings and Unicode UTF16LE (Wide Strings), and usually ignore anything in Asian languages as just being "code".

ExtractStrings doesn't, which means it picks up lots of "code" that isn't a string at all, but won't miss any text from a basic Korean, Chinese or Japanese game.exe either.

[SIZE=+1]Usage[/SIZE]
First of all, you should note that this program dose not have a GUI and double clicking it will do nothing interesting at all. You have to use it from the command line.

If you just run it, you will see something like this in your console:-
bobsobol - Extract Strings - Aid to Translation - RaGEZONE Forums
That should give you a fair idea of how to give it the information it needs to produce the information you need. But the next question you'll be asking yourself is "How do I know any of that?" Yes, I know, it's not very cleaver. But it doesn't actually need to be, because is incredibly cleaver and can give you everything needed for ExtractStrings.

The first thing you will need is a dump of a clean section of the file. All the text in PT is stored in the .Data and .RData sections... so we may as well dump both of those out to separate files now.

Open your game.exe in CFF Explorer, and immediately select Section Headers from the TreeView.
bobsobol - Extract Strings - Aid to Translation - RaGEZONE Forums
Okay, now dump your sections to files by Right clicking on them and selecting Dump Section.
bobsobol - Extract Strings - Aid to Translation - RaGEZONE Forums
You can name the files anything you like, but it might be a good idea to put them in the same folder as ExtractStrings. For the purpose of this exercise, lets say we call the .RData section game.rdt and the .Data section game.dat.

Don't close CFF Explorer yet! You also need to make a note of the Virtual address of those sections.
bobsobol - Extract Strings - Aid to Translation - RaGEZONE Forums
Once you've made a note of those, you can open your command line in the folder where you have ExtractStrings and your section files.
Now enter the following commands:-
Code:
ExtractStrings game.rdt 5C7000 > Game.txt
ExtractStrings game.dat 5FC000 >> Game.txt
Remember to use the offsets that you noted down from CFF Explorer. Mine where 001C7000 for .rdata and 001FC000 for .data. But, because the base address of the file is 400000, you need to add that, and get 005C7000 for .rdata and 005FC000 for .data.You can include the left hand 00 or not... that's entirely up to you.

Do note that the first command uses > (which will overwrite any existing file) and the second uses >> (which will append output to any existing file). This isn't anything I wrote, this is simply how the Command Console in Window NT handles output redirection. If you leave out everything from the first chevron (>) to the end of the line, you will get all the strings displayed on screen, instead of in a file.

This can be useful if you are a command line freak like me, and want to pass the output through SED, GREP or GSAR to perform some of the more basic filtering for you, or look for a specific line that you know is in there somewhere. But you needn't worry about it if you are not. I'm sure your preferred text editor has a "Search" or "Find" option. :wink:

Having done that, you'll have a file called game.txt which is pretty big, and starts off something like this:-
Code:
005C6FFFh = "6·"
005C7004h = "ㅀ"
005C7008h = "린"
005C700Ch = "뼭"
005C7014h = "V±"
005C701Ch = ""
005C701Fh = "€"
005C7024h = ":°"
005C7028h = "€°"
005C702Ch = "r°"
005C7030h = "f°"
005C7034h = "L°"
005C7038h = "沚"
005C703Ch = "$°"
005C7040h = "°"
005C7044h = "°"
005C7048h = "°"
005C704Ch = "啞"
All of those lines are pretty unimportant. Most lines which are only 1 to 3 characters long are offset tables, and not meant to be read as text at all.

However, further into the file you can see lines like these:-
Code:
005C8A4Ch = "> (%s) 아이템을 획득했습니다"
005C8A6Ch = "   크리스탈을 사용할수 없습니다   "
005C8A90h = "   소환은 한 번씩만 할수 있습니다   "
005C8AB8h = "  소환할수 있는 갯수를 초과했습니다  "
005C8AE0h = " 축하합니다. 상금 %d원을 획득했습니다 "
005C8B08h = "아이템 ( %s ) 사용기간이 종료 되어 제거되었습니다"
005C8B3Ch = "   아이템 ( %s ) 을 받았습니다   "
005C8B60h = "%s : 너무 말을 많이 했더니 혀가 굳어 버렸다! 우와 -_-;"
005C8B98h = "야호"
005C8BA0h = "무계를 초과했습니다        "
005C8BBCh = "귓말이 거부되었습니다"
005C8BD4h = "전투중에는 종료할수 없습니다"
005C8BF4h = "사용중인 창을 닫고 종료 하십시오"
005C8C18h = "게임을 종료 합니다"
005C8C2Ch = "서버와의 연결이 끊어 졌습니다 (4)"
005C8C50h = "서버와의 연결이 끊어 졌습니다 (3)"
005C8C74h = "서버와의 연결이 끊어 졌습니다 (2)"
005C8C98h = "서버와의 연결이 끊어 졌습니다 (1)"
005C8CBCh = "서버와의 연결이 끊어 졌습니다"
005C8CDCh = "%s님은 거리가 멀어서 거래 신청을 할수 없습니다"
This tells you exactly where to find those lines in Olly when you are translating.

Let's open the game.exe in Olly and look for the line "%s : 너무 말을 많이 했더니 혀가 굳어 버렸다! 우와 -_-;" Which is the "chatterbox" or "shut up" line "I talk so much my tongue froze up. Wow! -_-;".

According to the file we got from ExtractStrings, we should find that at 005C8B60. If you click in the Dump pane in Olly, press <Ctrl> + G or Right click -> Go to... -> Expression... then type (or paste) that address, Olly should take you right to that string.
bobsobol - Extract Strings - Aid to Translation - RaGEZONE Forums

There it is!​
[SIZE=+1]Downloads[/SIZE]
RapidShare AG, Cham, Switzerland - DEAD.
- DEAD.
- DEAD (copyright infringement my arse!)
- DEAD
zSHARE - ExtractStrings.zip - DEAD (and I still hate zShare)
Download ExtractStrings.zip for free on uploading.com - DEAD
Cloud storage... priceless. :D:
(they seem to do better than RapidShare alone) - DEAD
View attachment ExtractStrings.zip RaGEZONE attachment. Also priceless. :thumbup:

[SIZE=+1]Credits[/SIZE]
I must thank Osirus for pointing out how much easier this makes raw translation, and giving me ideas as to how to make the output more useful for this purpose.
 

Attachments

You must be registered for see attachments list
Last edited:
Moderator
Staff member
Moderator
Joined
Feb 22, 2008
Messages
2,404
Reaction score
724
I'm wondering, (sorry the swearing but) WHERE THE duck DID YOU LEARNED TO DO ALL THIS poop?

bob, I have no words. Even from the time when I was a newbie (I'm not telling I'm not anymore, it's just that Ive learned a few things) I saw you and, geez, this one is good! xD

Congratulations bob. You deserve it.
 
Custom Title Activated
Loyal Member
Joined
May 26, 2007
Messages
5,545
Reaction score
1,315
WHERE THE F*beep* DID YOU LEARNED TO DO ALL THIS S*beep*?
I learned to program, because it took 15 - 30 mins to load Space Invaders from an audio tape, and I could type the program in and play it in 10 mins.

Computers are primarily used for automating repetitive tasks. I was doing this job manually in Olly for a long long time, just as Osirus has probably been doing. Automating something you do constantly comes quite naturally to someone who knows how to program.

This is where the ideas to automate the SQL setup and even the entire PT server installation come from. Automate a task you have done a hundred times manually and are bored of doing the same thing over and over again.

I also used to manually fix the headers of the PCM wave files, TGAs and BMPs so I could open them in generic programs... but it gets boring really quick and you just write a program that does the boring stuff for you.

Do anything on a computer enough by hand and you find you have a routine... that routine becomes pseudo code that can be turned into a program really simply.

This one was a matter of loading the game into MadEdit, search and replace "0000" with "00" until there are no more, then search and replace "00" with "0D0A" and every time there is an null terminator it becomes a new line... pretty much everything else is readable as text.

ExtractStrings can keep a byte count as it's reading the file and tell me the offset of the start of each line. Actually, you could give it the entire game.exe and the offset 0, and it would tell you where to look in your Hex editor. (another thought that just occurred to me, and I'm sure DK would love XD) All the code in the .text section will be a mess that you'll probably want to trim from the text file very soon though.

It will also convert problematic characters, like Carriage Return (0x0D) and Line Feed (0x0A) into C style escape sequence. (\n\l) That's not hard either, and could have been done with Search and Replace in MadEdit, but the job is boring enough as it is.

Those where necessary for the .lng files because you can't put characters like that into a .ini style file like the .lng files International.dll uses. But it's not uncommon for them to be used in TextOut() or wsprintf() statements in the PTs' C source.

Speaking of which, my source code (as ever) is in the download too. It's not as clean or readable as some other programs I've released source for, and it's still in freeBASIC, because it was just meant to be a quick way to get a .lng file from a game.exe with no translation that you could then work into your own language. You're welcome to re-write it as Perl or C++ or C# or VB.net or whatever and give it a GUI if you like.

The easiest thing would be to make a front end to it if you really hate the command line. But as I say... I don't, I hate having to take one hand off my keyboard to use the mouse. XD
Congratulations bob. You deserve it.
I'm glad you like it. :wink:
 
Last edited:
Moderator
Staff member
Moderator
Joined
Feb 22, 2008
Messages
2,404
Reaction score
724
My last goal was to convert all the asp clan files to PHP. I was failing because, as you can see, there is a 0x0D that is returned after the Code=100.

I couldn't translate it to PHP. Who knows, I think I could dig up that. Well, no offence to sandurr, but his files is too much buggy. In a few years ago, I've delevoped a tool that can Create/Insert any clans in any server, without even being registered to it.
Well, lets stop with all that off-topic stuff. I just wanna know what you'd think about it.
 
Custom Title Activated
Loyal Member
Joined
May 26, 2007
Messages
5,545
Reaction score
1,315
PHP:
$return = "Code=100\r";
echo $return;
You mean like that?
 
Last edited:
Moderator
Staff member
Moderator
Joined
Feb 22, 2008
Messages
2,404
Reaction score
724
I was trying to do something like

$bla = "Code=100".0x0D;

I dont remember now. Clan was created 100%, but always returned error 7 I guess. But, I cant test it right now. I dont have clan files, and my pc is to overloaded to run a full (char+field) localserver.
 
Custom Title Activated
Loyal Member
Joined
May 26, 2007
Messages
5,545
Reaction score
1,315
But then
PHP:
$bla = "Code=100".0x0D;
echo $blah;
would surely produce
Code:
Code=10013
No?

I'm no good with PHP but since the . operator on strings is equivalent to the + operator, and numeric constants are automatically type cast to strings equivalent to their value...?
 
Skilled Illusionist
Joined
Apr 20, 2009
Messages
351
Reaction score
212
Numeric constant are casted to their ASCII equivalent (via the chr() function) when appended to a string I think.

In a few years ago, I've delevoped a tool that can Create/Insert any clans in any server, without even being registered to it.
You just need a web browser for that :p
 
Last edited:
Custom Title Activated
Loyal Member
Joined
May 26, 2007
Messages
5,545
Reaction score
1,315
Actually... you don't need web browser, you can use any HTTP client I believe. So something like , (maybe) (surely) or just using the APIs as PT does. :lol:

You can also use any client on port 80 of the destination IP to request and receive pages... if you know the right commands to give.
 
Last edited:
Moderator
Staff member
Moderator
Joined
Feb 22, 2008
Messages
2,404
Reaction score
724
But then
PHP:
$bla = "Code=100".0x0D;
echo $blah;
would surely produce
Code:
Code=10013
No?

I'm no good with PHP but since the . operator on strings is equivalent to the + operator, and numeric constants are automatically type cast to strings equivalent to their value...?
I havent thought about that.. I'll try it as soon as possible.
 
Custom Title Activated
Loyal Member
Joined
May 26, 2007
Messages
5,545
Reaction score
1,315
Numeric constant are casted to their ASCII equivalent (via the chr() function) when appended to a string I think.
You would probably know better than I, as I know you've worked with PHP a lot. But I've seen other code that does
PHP:
$somevar = "Some text = " + 23;
echo somevar;
type code which is supposed to just display
Code:
Some text = 23
So the fact that your constant is defined as hex in the source rather than decimal shouldn't make any difference?

And if the documentation I pointed to saying that . and + are the same thing where strings are concerned is to be believed... this is how I come to this conclusion. But I don't have php installed to try, and haven't used it enough to know off the top of my head.
 
Last edited:
Moderator
Staff member
Moderator
Joined
Feb 22, 2008
Messages
2,404
Reaction score
724
I was doing "Code=100".chr(0x0D) If I remember well.

This was my full code:
PHP:
<?if(CLAN_SYSTEM!=1) exit;?>
<?
/*
Totalmente escrito por Sheen COPYRIGHT 2010
Totalmente escrito por Sheen COPYRIGHT 2010
Totalmente escrito por Sheen COPYRIGHT 2010
Totalmente escrito por Sheen COPYRIGHT 2010
Totalmente escrito por Sheen COPYRIGHT 2010
*/

// V1.0 MAIO 2010
include_once "function/function.php";

$class = new Clan_Sheen;

$host = "(local)\SQLEXPRESS";
$pass = "123456";
$user = "sa";
// Variaveis passadas por query-sring pelo game.

$userid = $class->Pega('userid');
$lider = $class->Pega('chname');
$nome_clan = $class->Pega('clName');
$chtype = $class->Pega('chtype');
$level = $class->Pega('lv');

$frase_criar = "Clan Reformulado by Sheen";

if($userid == "" or $lider == "" or $invitado == "" or $chtype="" or $level == "")
{
	$class->Retornar(100);
}
$connection = odbc_connect($host,$user,$pass);

$query = "SELECT * FROM [ClanDB].[dbo].[UL] WHERE ChName = '$lider'";
$i=0;
$go = odbc_do($connection,$query);
while(odbc_fetch_row($go)) $i++;
$nome = odbc_result($go,"ClanName");
if($i>0 && $nome != "")
{
	$class->Retornar(2);
}elseif($i>0 && $nome == "")
{
	$query = "DELETE FROM [ClanDB].[dbo].[UL] WHERE ChName = '$lider' ";
	$go = odbc_exec($connection,$query);
}
$query = "SELECT ClanZang FROM CL WHERE ClanName='$nome_clan'"
$go = odbc_do($connection,$query);
$i=0;
while(odbc_fetch_row($go)) $i++;
if($i>0)
{
   $class->Retornar(3,0,true);
}
// Feita as validações, começa o processo de criação.
$query = "SELECT * FROM [ClanDB].[dbo].[LI] WHERE [ID] = '1' ";
$go = odbc_do($connection,$query);
$res = odbc_result($go,"IMG");
if($res != "")
{
	$iIMG = $res+1;
	$atualiza = "UPDATE [ClanDB].[dbo].[LI] SET [IMG] = '$iIMG' WHERE ID='1' ";
	$go = odbc_exec($connection,$atualiza);
}else{
	$iIMG = 1;
	$atualiza = "INSERT INTO [ClanDB].[dbo].[LI] ([IMG],[ID]) VALUES ('1','1')";
	$go = odbc_exec($connection,$atualiza);
}
// Imagem definida, continuar.
$query1 = "INSERT INTO CL ([ClanName],[UserID],[ClanZang],[MemCnt],[Note],[MIconCnt],[RegiDate],[LimitDate],[DelActive],[PFlag],[KFlag],[Flag],[NoteCnt],[Cpoint],[CWin],[CFail],[ClanMoney],[CNFlag],[SiegeMoney]) values('$nome_clan','$userid','$lider','1','$frase_criar','$iIMG',getdate(),getdate()+3600,'0','0','0','0','1','0','0','0','0','0','0')";
$query2 = "SELECT * FROM [ClanDB].[dbo].[CL] WHERE [ClanName] = '$nome_clan' ";

$go = odbc_do($connection,$query2);
$IDX = odbc_result($go,"IDX");

$query3 = "INSERT INTO UL ([IDX],[userid],[ChName],[ClanName],[ChType],[ChLv],[Permi],[JoinDate],[DelActive],[PFlag],[KFlag],[MIconCnt]) values('$IDX','$userid','$lider','$nome_clan','$chtype','$level','0',getdate(),'0','0','0','$iIMG')";
 // criando.
 
$go = odbc_exec($connection,$query1);
$go2 = odbc_exec($connection,$query3);
if($go && $go2)
{
	odbc_close($connection);
	$class->Retornar(1,500000,true);// O 500 000 seria o valor de gold a ser cobrado, supostamente nossos games não tem esta função funcionandao corretamente.(CMoney)
}
?>
<?
define("CLAN_SYSTEM",1);
class Clan_Sheen
{
	public $split;
	
	function __construct()
	{
		$this->split = chr(0x0D);
	}
	function Pega($value)
	{
		return trim($_GET[$value],"\x00");
	}
	function Retornar($codigo,$mix="",$money=false)
	{
		if($mix!="" && $money == true)
		{
			$str = "Code=".$codigo.$this->split."CMoney=".$mix.$this->split;
		}else
		{
			$str = "Code=".$codigo.$this->split;
		}
		echo $str;
		exit;
	}
}
?>
Clan is created 100%, but with Error 7 I guess.
bob, I'll make another thread if you want, to stop OT.
 
Last edited:
Custom Title Activated
Loyal Member
Joined
May 26, 2007
Messages
5,545
Reaction score
1,315
PHP:
    function Retornar($codigo,$mix="",$money=false)
    {
        if($mix!="" && $money == true)
        {
            $str = sprintf("Code=%s\rCMoney=%s\r", $codigo, $mix);
        }else
        {
            $str = sprintf("Code=%s\r", $codigo);
        }
        echo $str;
        exit;
    }
I believe that should work, but is mixing your OO metaphor with a procedural one. :s (But the seems to approve of that)
 
Skilled Illusionist
Joined
Apr 20, 2009
Messages
351
Reaction score
212
Do you guys remember the post I made a long time ago about with the Wireshark capture showing a few differences between ASP/IIS and PHP/ISS return messages ?
Different HTTP headers, ASP returning 1 packet and PHP returning 2 packets all the time.
 
Custom Title Activated
Loyal Member
Joined
May 26, 2007
Messages
5,545
Reaction score
1,315
Do you guys remember the post I made a long time ago about with the Wireshark capture showing a few differences between ASP/IIS and PHP/ISS return messages ?
Different HTTP headers, ASP returning 1 packet and PHP returning 2 packets all the time.
I do. I thought you also posted a means of setting up PHP for IIS which didn't produce this problem. Am I mistaken? (I mean, I know it's harder, but I thought it was possible when done with care.)
 
Custom Title Activated
Loyal Member
Joined
Jan 28, 2009
Messages
1,320
Reaction score
616
Do you guys remember the post I made a long time ago about with the Wireshark capture showing a few differences between ASP/IIS and PHP/ISS return messages ?
Different HTTP headers, ASP returning 1 packet and PHP returning 2 packets all the time.

I don't remember any packets but I think you said that it might work with new version of PHP? 5.1, 5.2 back than?
 
Custom Title Activated
Loyal Member
Joined
May 26, 2007
Messages
5,545
Reaction score
1,315
Something like that. I suspect it was in one of the "deleted" guides here, and I don't remember specifics.

There is also a packet merging & caching filter for CGI (one specifically for php IMS) interfaces to IIS... so maybe it was something like that?
 
Imri Persiado
Joined
May 17, 2008
Messages
941
Reaction score
26
amazing guide bob but when I open the ExtractString.bat it closed automaticly.
 
Custom Title Activated
Loyal Member
Joined
May 26, 2007
Messages
5,545
Reaction score
1,315
What parameters did you pass it?
What error does it display in your console?
(I'm actually thinking that you didn't run it from the console and so didn't pass it any parameters, but that may be doing you a dis-service)
First of all, you should note that this program dose not have a GUI and double clicking it will do nothing interesting at all. You have to use it from the command line.
 
Back
Top