DAZZLE S UPGRADE PACK

If you haven't already done so, upgrade your game by downloading Dazzle's all-in-one upgrade pack. It comes with everything you need for today's servers. Does your blue bar freeze when joining servers? Do you lag in games? Do you get an annoying siren in Phobik's Servers? This is what you need. CLICK HERE TO DOWNLOAD.

Search found 6 matches

Return

Re: Any ways of bringing TT back?

It's sad that the game is dead, it was a huge part of my childhood. It's the only community left where I still use my ancient nickname "ArtCrazy".

To be fair, the source code isn't strictly necessary for a lot of things. The executable isn't obfuscated in any way (especially if you find a cracked version *somewhere*), so with my current skills a "ThinkTanks Script Extender" wouldn't be impossible. Looking at the open-sourced Torque 3D code, adding extra scripting functionality doesn't seem too hard, mostly a matter of figuring out where to hook!

The DSO format is basically "Torque Script bytecode", and could also be easily disassembled and/or decompiled given some time as there's almost a 1:1 relationship to the actual script (there is a project called Untorque that does that for newer T3D versions; sadly it's not compatible with TT). The only difficulty here would be the fact that the DSO bytecode version used by TT is considerably older than the current T3D version, so the bytecode specification probably differs quite a bit. Still, that could be easily reverse engineered since we can create/compile arbitrary .cs files with only specific commands, and look at the output.

It could even be possible, given enough work, to reverse engineer TT specifics (GUI, DSO, tank treads, and all other things incompatible with T3D), decompile all torque script files, and finally import it into the newer open-sourced engine version. Of course, that could be in a legally gray area unless we had permission from IA/GG (I guess there'd be no issue if we kept it strictly non-profit and/or forced the user to prove they own a copy of ThinkTanks, but IANAL), and I probably don't have enough C++ reverse engineering experience to pull it off.

I can only imagine how far we would have gotten if 10 years ago I would have had the programming/reverse engineering knowledge I do today. And I don't doubt that other modders are in a similar situation.

If I had time, I might actually try and play around with this stuff, get my reverse engineering skills back up to speed (haven't done a game mod since I patched Age of Empires 2 to support any resolution 4 or so years ago; and then contributed the code to the community patch Userpatch when that started being used). Thing is, I'm currently finishing my Master's Thesis (1 month and a half to go!), and that + my job search has priority.
by ArtCrazy
Fri Mar 04, 2016 3:08 pm
 
Jump to forum
Jump to topic

Re: Any ways of bringing TT back?

Yeah, I'll give it a try one of these days, figure out if my initial estimates are correct, and how much work it would take to do all the stuff I mentioned. I'd like to at least build a TT DSO decompiler or something - not necessarily to get much use out of it, considering how dead the game is - mostly out of curiosity, since I used to spend ages trying to figure out how to do stuff in this game years ago, and I've always wanted to know exactly how much I had been able to figure out back then!

Still, real life stuff takes priority (thesis and job search), so don't expect anything any time soon! And no guarantees I'll ever be able to get anything working - but I want to at least try ;)

EDIT: So, I had some time today and decided to check how different the TT DSO implementation is from the latest T3D engine. It's quite different, but in about 4 hours work I've been able to reverse engineer the opcodes list, by comparing the disassembled version in the ThinkTanks executable with the latest T3D engine's source code. There are a ton of differences, and as such I'm not 100% sure the mapping is correct (and there's 2 opcodes for which I can't find a corresponding one in T3D, plus 3 opcodes which don't seem to do anything).

Assuming I had more time, the next step would be to implement a disassembler, using this opcode mapping (wouldn't take long, most of the code can be borrowed from the T3D sources). Then, make very simple "very few instructions" .cs files, compile them, and see what the disassembly looks like. Knowing the script that went into it, the opcode mapping could easily be corrected.

Anyways, if anyone is interested in picking up where I've stopped, this is my current opcode mapping (corresponds to the enum with the same name in Engine/source/console/compiler.h ):

enum CompiledInstructions
{
OP_FUNC_DECL = 0,
OP_CREATE_OBJECT = 1,
OP_2_INEXISTENT = 2,
OP_3_INEXISTENT = 3,
OP_ADD_OBJECT = 4,
OP_END_OBJECT = 5,
OP_JMPIFFNOT = 6,
OP_JMPIFNOT = 7,
OP_JMPIFF = 8,
OP_JMPIF = 9,
OP_JMPIFNOT_NP = 10,
OP_JMPIF_NP = 11,
OP_JMP = 12,
OP_INVALID = 13,
OP_CMPEQ = 14,
OP_CMPGR = 15,
OP_CMPGE = 16,
OP_CMPLT = 17,
OP_CMPLE = 18,
OP_CMPNE = 19,
OP_XOR = 20,
OP_MOD = 21,
OP_BITAND = 22,
OP_BITOR = 23,
OP_NOT = 24,
OP_NOTF = 25,
OP_ONESCOMPLEMENT = 26,
OP_SHR = 27,
OP_SHL = 28,
OP_AND = 29,
OP_OR = 30,
OP_ADD = 31,
OP_SUB = 32,
OP_MUL = 33,
OP_DIV = 34,
OP_NEG = 35,
OP_SETCURVAR = 36,
OP_SETCURVAR_CREATE = 37,
OP_SETCURVAR_ARRAY = 38,
OP_SETCURVAR_ARRAY_CREATE = 39,
OP_LOADVAR_UINT = 40,
OP_LOADVAR_FLT = 41,
OP_LOADVAR_STR = 42,
OP_LOADVAR_VAR = 43,
OP_SAVEVAR_UINT = 44,
OP_SAVEVAR_FLT = 45,
OP_SAVEVAR_STR = 46,
OP_SAVEVAR_VAR = 47,
OP_30_UNKNOWN = 48,
OP_31_UNKNOWN = 49,
OP_LOADFIELD_UINT = 50,
OP_LOADFIELD_FLT = 51,
OP_LOADFIELD_STR = 52,
OP_SAVEFIELD_UINT = 53,
OP_SAVEFIELD_FLT = 54,
OP_SAVEFIELD_STR = 55,
OP_STR_TO_UINT = 56,
OP_STR_TO_FLT = 57,
OP_STR_TO_NONE = 58,
OP_FLT_TO_UINT = 59,
OP_FLT_TO_STR = 60,
OP_FLT_TO_NONE = 61,
OP_UINT_TO_FLT = 62,
OP_UINT_TO_STR = 63,
OP_UINT_TO_NONE = 64,
OP_LOADIMMED_UINT = 65,
OP_LOADIMMED_FLT = 66,
OP_TAG_TO_STR = 67,
OP_LOADIMMED_STR = 68,
OP_LOADIMMED_IDENT = 69,
OP_CALLFUNC_RESOLVE = 70,
OP_CALLFUNC = 71,
OP_42_INEXISTENT = 72,
OP_ADVANCE_STR = 73,
OP_ADVANCE_STR_APPENDCHAR = 74,
OP_ADVANCE_STR_COMMA = 75,
OP_ADVANCE_STR_NUL = 76,
OP_REWIND_STR = 77,
OP_TERMINATE_REWIND_STR = 78,
OP_COMPARE_STR = 79,
OP_PUSH = 80,
OP_PUSH_UINT = 81,
OP_PUSH_FLT = 82,
OP_PUSH_VAR = 83,
OP_PUSH_FRAME = 84,
};

The switch statement inside CodeBlock::exec ( Engine/source/console/compiledEval.cpp ), which is the actual bytecode interpreter, and was what I used to reverse engineer the above mappings, is located at ThinkTanks.exe memory address 0x0040EC75 (on Windows).

tl;dr: The DSO disassembler and decompiler should be pretty easy to get working, so we're in luck at least on that front!
by ArtCrazy
Sat Mar 05, 2016 3:59 pm
 
Jump to forum
Jump to topic

ThinkTanks Script Decompiler

The DSO format is a very simple bytecode language, surprisingly easy to decode and decompile, especially since we have access to an open-source implementation of the compiler. The hard part was actually figuring out all the changes since ThinkTanks was released, since ThinkTanks uses DSO specification 21, over 10 years old, and a lot has changed since then. Another project called Untorque already exists to decompile the newer versions of DSO files, but it's way too recent to be compatible with ThinkTanks.

That's where I come in. As a way to brush up my reverse engineering and C++ skills, which had fallen to disuse over the last few years, I spent about 10 hours over the last couple of days reverse engineering all implementation differences between ThinkTanks and the latest version of Torque 3D, and wrote a DSO parser and disassembler. Finally, I adapted the aforementioned Untorque , removing all the dependencies on the new Torque 3D engine (basing it instead on my DSO parser/disassembler), and then adapted it to the older ThinkTanks DSO format.

Download a Windows binary here
View the source code

I've tested it on a ton of my scripts, and they all seem to have decompiled correctly. I was also able to decompile tank.cs, for example. However, I cannot guarantee there won't be mistakes or problems with some combinations of DSO opcodes, and I have no idea if the Mac/Linux version of ThinkTanks uses the same DSO specification as Windows (I distinctly remember scripts needing to be recompiled for Mac, the cause is probably that the bytecode specification is slightly different).

Again: There will likely be issues and bugs that I haven't caught! Use at your own risk. (for example, backup all your DSO files first)

For example, here's a decompiled tank.cs.dso using this application (obtained by running "ThinkTanksScriptDecompiler.exe tank.cs.dso --decompile > tank.cs"):
exec("./tankFx.cs");
exec("./tankDb.cs");
exec("./tankAI.cs");
function TankData::create(%block)
{
if (%block $= "LightTank")
{
%obj = new Tank("")
{
.dataBlock = %block;
};
return %obj;
}
else
{
if (%block $= "MediumTank")
{
%obj = new Tank("")
{
.dataBlock = %block;
};
return %obj;
}
else
{
if (%block $= "HeavyTank")
{
%obj = new Tank("")
{
.dataBlock = %block;
};
return %obj;
}
}
}
return -1;
return ;
}
function TankData::onAdd(%this, %obj)
{
return ;
}
function TankData::onSPDestroyed(%db, %this, %killer)
{
if (%this.client == 0)
{
$Game::SPBots = $Game::SPBots - 1;
$Game::DeadBots = $Game::DeadBots + 1;
if ((isObject(%killer) && isObject(%killer.client)) && (%killer.client.lives > 0))
{
%pts = 0;
%startScore = strfrap(strswiz($Game::IdleText @ "client", 12), %killer.client.spscore);
if (strstr(%db.getName(), "Bronze") != -1)
{
%pts = $Game::BronzePoints;
%killer.client.bronzeKills = %killer.client.bronzeKills + 1;
}
else
{
if (strstr(%db.getName(), "Silver") != -1)
{
%pts = $Game::SilverPoints;
%killer.client.silverKills = %killer.client.silverKills + 1;
}
else
{
if (strstr(%db.getName(), "Gold") != -1)
{
%pts = $Game::GoldPoints;
%killer.client.goldKills = %killer.client.goldKills + 1;
}
else
{
if (strstr(%db.getName(), "Boss") != -1)
{
%pts = $Game::BossPoints;
%killer.client.bossTankKilled = %killer.client.bossTankKilled + 1;
}
}
}
}
%time = getRealTime();
if ((%time - %killer.client.lastKillTime) < $Game::QuickShotTime)
{
%killer.client.quickKill = %killer.client.quickKill + 1;
%pts = (%pts * 5) * %killer.client.quickKill;
commandToClient(%killer.client, 'BottomPrint', "x" @ 5 * %killer.client.quickKill SPC "Quick-shot bonus!", 2, 2);
alxPlay(SPQuick);
}
else
{
%killer.client.quickKill = 0;
}
%killer.client.lastKillTime = %time;
%newScore = %startScore + %pts;
%killer.client.spscore = strfrip(strswiz($Game::IdleText @ "client", 12), %newScore);
SPScoreGui.setScore(%newScore);
%freeLives = mFloor(%newScore / 10000) - mFloor(%startScore / 10000);
if (%freeLives > 0)
{
alxPlay(SPExtra);
%killer.client.lives = %killer.client.lives + %freeLives;
SPLivesGui.showLives(%killer.client.lives);
}
}
else
{
%newdb = "GoldHeavyTank";
$Game::DeadBots = $Game::DeadBots - 1;
$Game::SPTotalBots = $Game::SPTotalBots - 1;
spawnBotSP(%newdb);
}
}
else
{
%this.client.lives = %this.client.lives - 1;
%this.client.deaths = %this.client.deaths + 1;
SPLivesGui.showLives(%this.client.lives);
if (%this.client.lives > 0)
{
if ($Game::DemoMode)
{
%this.client.player = 0;
%this.client.spawnPlayer();
}
else
{
commandToClient(%this.client, 'CenterPrint', "Your brain has been separated from your tank, press SPACE.", 0, 2);
}
}
else
{
if ($Game::DemoMode)
{
%this.client.player = 0;
%this.client.spawnPlayer();
}
else
{
commandToClient(%this.client, 'CenterPrint', "G A M E O V E R", 0, 2);
%idx = $playerList::lastSelection;
%score = strfrap(strswiz($Game::IdleText @ "client", 12), %this.client.spscore);
if (%score > getSolohiscore(%idx))
{
%swiz = strswiz(%idx @ $playerList::playerName[%idx] @ $Game::IdleText, 12);
$playerList::hiscore[%idx] = strfrip(%swiz, %score) @ ;
if (!isDemo())
{
export("$playerList::*", "~/client/players.cs");
}
}
}
}
}
return ;
}
function TankData::onTargetDestroyed(%db, %this, %killer)
{
%isPlayerKilled = isObject(%this.client);
%isPlayerKiller = isObject(%killer.client);
if (%isPlayerKiller)
{
%killer.incScore(1, 1);
%killer.client.kills = %killer.client.kills + 1;
if ((%killer.client.kills % 10) == 0)
{
spawnTarget(TargetSaucer);
}
}
if (%isPlayerKilled)
{
commandToClient(%client, 'CenterPrint', "Your brain has been separated from your tank, press SPACE.", 0, 1);
}
else
{
if (!(%this.dataBlock.getName() $= "TargetSaucer"))
{
spawnTarget(%this.dataBlock);
}
}
return ;
}
function TankData::onDestroyed(%db, %this, %killer)
{
if ($Game::singlePlayer)
{
%db.onSPDestroyed(%this, %killer);
return ;
}
if ($Game::TargetRange)
{
%db.onTargetDestroyed(%this, %killer);
return ;
}
%client = %this.client;
%client.deaths = %client.deaths + 1;
if (isObject(%killer))
{
messageAll('MsgClientKilled', '%1 has been eliminated by %2!', %client.name, %killer.client.name);
if ($Game::MissionType $= "Deathmatch")
{
if ($Game::TeamGame)
{
if (%killer.client.team.getId() != %client.team.getId())
{
%killer.incScore(1, 1);
}
}
else
{
%killer.incScore(1, 1);
}
}
%killer.client.kills = %killer.client.kills + 1;
}
else
{
messageAll('MsgClientKilled', '%1 has been eliminated!', %client.name);
}
if ($Game::MissionType $= "Deathmatch")
{
if ($Game::TeamGame)
{
if (isObject(%killer) && (%killer.client.team.getId() == %client.team.getId()))
{
%this.incScore(-1, -1);
}
else
{
%this.incScore(-1, 0);
}
}
else
{
%this.incScore(-1, -1);
}
}
if (!isObject(%client.ai))
{
if ($Game::aiControlMode)
{
%client.player = 0;
%client.schedule(4000, "spawnPlayer");
}
else
{
commandToClient(%client, 'CenterPrint', "Your brain has been separated from your tank, press SPACE.", 0, 1);
}
}
else
{
spawnBotPlayer(%client);
}
return ;
}
function Tank::onRemove(%this)
{
return ;
}
function Tank::incScore(%this, %delta, %deltaTeam)
{
%client = %this.client;
if (-%delta > %client.score)
{
%client.cumScore = %client.cumScore - %client.score;
%client.score = 0;
}
else
{
%client.score = %client.score + %delta;
%client.cumScore = %client.cumScore + %delta;
}
if (isObject(%client.team))
{
if (-%deltaTeam > %client.team.score)
{
%client.team.cumScore = %client.team.cumScore - %client.team.score;
%client.team.score = 0;
}
else
{
%client.team.score = %client.team.score + %deltaTeam;
%client.team.cumScore = %client.team.cumScore + %deltaTeam;
}
}
messageAll('MsgClientScoreChanged', "", %client.score, %client.cumScore, %client);
if (isObject(%client.team))
{
messageAll('MsgTeamScoreChanged', "", %client.team.score, %client.team.cumScore, %client.team.getId());
}
return ;
}

Not sure how many people are even going to see this (that's why I'm posting in the general forum instead of the Modding one), but this could open quite a lot of doors for modding. The possibility I'm most interested in, however, is decompiling all .dso files, and porting the game over to a new open-source version of the Engine. It might be much more difficult to do than simply copy+pasting stuff, as there are ThinkTanks-specific engine implementations of stuff (GuiControl profiles, Tank datablocks, script callbacks, and some more things) which would need to be reverse engineered and re-implemented (and it'll be much more difficult than decompiling DSO files - most likely out of my league).

That's all from me for a few months, though, as I'm currently writing my Master's thesis! I suggest someone plays around with this, and sees what they can find. Specifically, I'm curious to know how much works if you decompile all ThinkTanks scripts and delete the DSO files (i.e., if the decompiler is working perfectly!) - if not the case, then there's probably some bugs for some very weird cases. In addition, assuming everything works, I'd be interested in knowing what happens if you decompile everything, and replace the ThinkTanks executable with one taken from the latest Torque 3D version. Most things should not work, but I wouldn't be surprised if the menus worked more or less correctly! (Which would be great news)

-----------------

Note for the technically curious, the DSO format is basically a list of strings, floats, and then Torque bytecode (machine code). It runs by being loaded into a Virtual Machine (with a stack-based architecture) that is part of the torque engine.

For example, this code
if ($cond)
{
%bla = 2;
}

is compiled into this list of opcodes (obtained by running "ThinkTanksScriptDecompiler file.cs.dso --disassemble > file.disasm", header and comments added manually):
ADDRESS : OPCODE_IN_HEX/OPCODE_IN_DEC HEX_DUMP : DISASSEMBLED VIEW
0x00000000 : 0x24/36 00000024 G00 : OP_SETCURVAR var=$cond // Set $cond as current variable
0x00000002 : 0x29/41 00000029 : OP_LOADVAR_FLT // Load current variable ($cond) as a float value to the stack
0x00000003 : 0x06/06 00000006 0000000B : OP_JMPIFFNOT ip=0x0000000B // Jump to address 0xB if the value on top of the stack ($cond) is 0
0x00000005 : 0x41/65 00000041 00000002 : OP_LOADIMMED_UINT val=2 // Load immediate "2" to the top of the stack
0x00000007 : 0x25/37 00000025 G01 : OP_SETCURVAR_CREATE var=%bla // Set current variable to %bla
0x00000009 : 0x2B/43 0000002B : OP_SAVEVAR_UINT // Save top of the stack ("2") to the current var (%bla)
0x0000000A : 0x40/64 00000040 : OP_UINT_TO_NONE // Pop top of the stack
0x0000001B : 0x0D/13 0000000D : OP_RETURN // Return, also used to indicate "end of file"

"G00" and "G01" in the HEX_DUMP section are simply names the disassembler gives to the string identifiers, and can be listed by using the "--strings" parameter as well:
G00 (os=0x00000000) = "$cond"
G01 (os=0x00000006) = "%bla"

All the application does, is to figure out what the combinations of opcodes meant in the original script. While a rather complex process, the opcodes map very closely with the scripts, so almost 1:1 compilation->decompilation is feasible. The fact that Untorque already existed saved me a ton of work on that front.
by ArtCrazy
Mon Mar 07, 2016 10:25 pm
 
Jump to forum
Jump to topic

Re: Any ways of bringing TT back?

So... Bored by the writing of my thesis, I decided to take the weekend off working on the DSO decompiler, and it's more or less done (with some bugs). Read more here!

That's all from me for a few months, though! I suggest someone plays around with it, and sees what they can find. Specifically, I'm curious to know how much works if you decompile all ThinkTanks scripts and delete the DSO files (i.e., if the decompiler is working perfectly!) - if not the case, then there's probably some bugs for some very weird cases. In addition, assuming everything works, I'd be interested in knowing what happens if you decompile everything, and replace the ThinkTanks executable with one taken from the latest Torque 3D version. Most things should not work, but I wouldn't be surprised if the menus worked more or less correctly! (Which would be great news)
by ArtCrazy
Mon Mar 07, 2016 10:27 pm
 
Jump to forum
Jump to topic

Re: It's 12:31 am and I need sleep

You're not the only one to miss the game. I'm currently 24, and started playing TT at 12 or so. Yeah, I've spent as much time alive after being introduced to ThinkTanks, as I did before then ;)

After some time of playing, I started to dabble into modding and scripting. At first, I was terrible, but I spent like one whole year playing around with dash's codes and slowly self-taught myself how to program in Torque Script. The last few years of the game, I was mostly a "full-time scripter", and at one point I'm not even sure if I played the game more than a couple of hours per year. However, I always really enjoyed modding and scripting the game.

Today I'm about to graduate as an Electrical and Computer Engineer, specialized in Computer Architecture. It might be fair to say that without TT, I might not be where I am today. Not saying I would have taken a completely different path, but ThinkTanks was a very important part of my childhood and helped shape my interests, and be the man I am today. It is really sad to see that it is as dead, but I will always treasure all the memories I have of all the time I spent on PTT, or playing/modding/scripting ThinkTanks.

I don't really remember you (Downgrade) very well, but I believe we had quite a few interactions back then.

Once I get more stable (find a job, etc), I've been planning to spend some time looking into a possible "ThinkTanks reborn", by porting the game into the latest T3D version. Maybe then we could get some new blood into the game! It's probably not going to be easy (if even possible), and even if it does, we're not guaranteed to find a community, but I owe it to the game to at least try :)

PS: I just noticed that the PTT1 archives are actually back up! I thought they had been lost forever, and all we had was the Internet Wayback Machine. I would love to get a copy of the whole archive, does anyone know who I could contact?
by ArtCrazy
Sun May 15, 2016 9:12 pm
 
Jump to forum
Jump to topic

Re: It's 12:31 am and I need sleep

ThatGuySam wrote:Hey Art,

Check ur PMs.


Thanks! I've replied :)
by ArtCrazy
Sun May 22, 2016 2:30 pm
 
Jump to forum
Jump to topic