Alright. I've asked this question in WDYNHW a few months ago but it was left unanswered, I've also spent hours on trying with different debuggers and methods of figuring this out but I am at absolute zero progress and thus I've now created this thread.
Farming Simulator 2013 stores all of its vehicles in XML files with the ending .i3d. These contain all information, and on smaller models, also the modeldata. But for larger models, there's a separate file with the ending .i3d.shapes containing the modeldata, and that's where I'm stumped, I'm trying to read it.
What I know so far:
* They are -most likely- compressed. I've compared the hex of multiple different shapes and I see no patterns or relevant information in the beginning of the file, I can neither find any plaintext whatsoever.
What I've tried so far:
* I got access to an editor which can load up the .shapes file just fine, so I thought about capturing its reading part and then using the assembly to figure out stuff.
* Using very limited knowledge of OllyDbg, I've tried to do the above with no results, because I have no idea what I'm doing.
* I've downloaded 2 different "signature scan" commandline programs which claims to be able to find most compression algorithms out there, but 0% match with both.
* I've googled a whole lot with no results.
And that's about it. I got plenty of files to look at and compare, and here's a screenshot of 3 different files, incase I missed something:
[IMG]http://puu.sh/bDbdc/b0a058c569.png[/IMG]
Modeleditor: [URL]https://dl.dropboxusercontent.com/u/15075155/GIANTS_Editor_5.5.2_win32.zip[/URL]
The .shapes files: [URL]https://dl.dropboxusercontent.com/u/15075155/3shapefiles.zip[/URL]
I3D file for "kroneBigPack": [url]https://dl.dropboxusercontent.com/u/15075155/kroneBigPack1290.zip[/url]
The editor is able to export into obj, and here's the output of the "kroneBigPack": [URL]https://dl.dropboxusercontent.com/u/15075155/kroneBigPack1290.obj[/URL]
I appreciate any help.
What about the accompanying XML files?
[editline]17th September 2014[/editline]
As in, .i3d
Posted in the OP, not sure what useful information you can find there cause only 2 lines are relevant to this thread.
Given you can't open the shapes files on their own, they would be of help if someone with more time and reversing experience than me wanted to look at the editor opening a shapes file in a debugger.
Files are compressed for sure prob with lzma Ill take a look for you tomorrow
-edit-
[IMG]http://puu.sh/c2qkh.png[/IMG]
sub_13E1490 (
---> int __stdcall sub_1D7BE0(int, void *, char)
that loads the .i3d
reads from 0 to 4096 stores it somewhere in a buffer
the first 4bytes of it doesn't seem encrypted/compressed probably the compression decode bytes.
[IMG]http://puu.sh/c2qrJ.png[/IMG]
the first 2 bytes change the rest (0x0,0x02) are const I'm still looking at what it actually does with this data
Interesting findings, looking forward for more progress!
Interessing....
From my test i've discover thath:
Open i3d with normal shape file:
reading frist 4096 bytes,next 4096 bytes,next 4096.
finally it start reading shape from offeset 8192.
Open i3d with cripted shape file:
reading frist 4096 bytes,reading frist 4096 bytes,reading next 4096 bytes,
finally it start reading shape from offeset 4096.
This protected shape are parts of some DLC, and i've extracted dlc, fixed all encripted script then put into game like as normal mod. And it works until you try to buy element with shape cripted. this made game crashes.
I've figured this have different function for loading dlc and standar mod,then i've decripted base data from the game and i've find function thath load mod and dlc(it's lua script):
[CODE]function loadDlcs()
if g_isDemo or g_isGamescomVersion then
return
end
for i = 1, table.getn(g_dlcsDirectories) do
local dir = g_dlcsDirectories[i]
if dir.isLoaded then
loadDlcsFromDirectory(dir.path)
end
end
end
function loadDlcsFromDirectory(dlcsDir)
createFolder(dlcsDir)
local files = Files:new(dlcsDir)
for k, v in pairs(files.files) do
local addDLCPrefix = false
local dlcFileHash, dlcDir, xmlFilename
if v.isDirectory then
if g_isDevelopmentVersion then
dlcDir = v.filename
xmlFilename = "dlcDesc.xml"
addDLCPrefix = true
end
else
local len = v.filename:len()
if len > 4 then
local ext = v.filename:sub(len - 3)
if ext == ".dlc" then
dlcDir = v.filename:sub(1, len - 4)
dlcFileHash = getFileMD5(dlcsDir .. "/" .. v.filename, dlcDir)
xmlFilename = "dlcDesc.xml"
addDLCPrefix = true
elseif ext == ".zip" or ext == ".gar" then
dlcDir = v.filename:sub(1, len - 4)
dlcFileHash = getFileMD5(dlcsDir .. "/" .. v.filename, dlcDir)
xmlFilename = "modDesc.xml"
addDLCPrefix = false
end
end
end
if dlcDir ~= nil and xmlFilename ~= nil and g_dlcModNameHasPrefix[dlcDir] == nil then
local absDlcDir = dlcsDir .. "/" .. dlcDir .. "/"
local dlcFile = absDlcDir .. xmlFilename
g_dlcModNameHasPrefix[dlcDir] = addDLCPrefix
loadModDesc(dlcDir, absDlcDir, dlcFile, dlcFileHash, dlcsDir .. "/" .. v.filename, v.isDirectory, addDLCPrefix)
end
end
end
function loadMods()
local modsDir = g_modsDirectory
if g_isDemo or g_isGamescomVersion then
return
end
g_showIllegalActivityInfo = false
local files = Files:new(modsDir)
for k, v in pairs(files.files) do
local modFileHash, modDir
if v.isDirectory then
modDir = v.filename
else
local len = v.filename:len()
if len > 4 then
local ext = v.filename:sub(len - 3)
if ext == ".zip" or ext == ".gar" then
modDir = v.filename:sub(1, len - 4)
modFileHash = getFileMD5(modsDir .. "/" .. v.filename, modDir)
end
end
end
if modDir ~= nil then
local absModDir = modsDir .. "/" .. modDir .. "/"
local modFile = absModDir .. "modDesc.xml"
loadModDesc(modDir, absModDir, modFile, modFileHash, modsDir .. "/" .. v.filename, v.isDirectory, false)
end
end
if g_showIllegalActivityInfo then
print("Info: This game protects you from illegal activity")
end
g_showIllegalActivityInfo = nil
end
function loadModDesc(modName, modDir, modFile, modFileHash, absBaseFilename, isDirectory, addDLCPrefix)
if not getIsValidModDir(modName) then
print("Error: Invalid mod name '" .. modName .. "'! Characters allowed: (_, A-Z, a-z, 0-9). The first character must not be a digit")
return
end
local origModName = modName
if addDLCPrefix then
modName = g_uniqueDlcNamePrefix .. modName
end
if g_modNameToDirectory[modName] ~= nil then
return
end
g_modNameToDirectory[modName] = modDir
local isDLCFile = false
if Utils.endsWith(modFile, "dlcDesc.xml") then
isDLCFile = true
if not fileExists(modFile) then
g_hasLicenseError = true
print("Error: No license for dlc " .. modName .. ". Please reinstall.")
return
end
end
if isDLCFile then
print("Load dlc: " .. modName)
else
print("Load mod: " .. modName)
end
local xmlFile = loadXMLFile("ModFile", modFile)
local modDescVersion = getXMLInt(xmlFile, "modDesc#descVersion")
if modDescVersion == nil then
print("Error: Missing descVersion attribute in mod " .. modName)
delete(xmlFile)
return
end
if modDescVersion ~= 9 and modDescVersion ~= 10 and modDescVersion ~= 11 and modDescVersion ~= 12 and modDescVersion ~= 13 and modDescVersion ~= 14 and modDescVersion ~= 15 and modDescVersion ~= 16 then
print("Error: Unsupported mod description version in mod " .. modName)
delete(xmlFile)
return
end
if _G[modName] ~= nil then
print("Error: Invalid mod name '" .. modName .. "'")
delete(xmlFile)
return
end
if isDLCFile then
local requiredModName = getXMLString(xmlFile, "modDesc.multiplayer#requiredModName")
if requiredModName ~= nil and requiredModName ~= origModName then
print("Error: Do not rename dlcs. Name: '" .. origModName .. "'. Expect: '" .. requiredModName .. "'")
delete(xmlFile)
return
end
end
local modEnv = {}
_G[modName] = modEnv
local modEnv_mt = {__index = _G}
setmetatable(modEnv, modEnv_mt)
if not isDLCFile then
modEnv._G = modEnv
end
modEnv.g_i18n = I18N:new(false)
I18N.initModI18N(modEnv.g_i18n, g_i18n, modName)
function modEnv.loadstring(str, chunkname)
str = "setfenv(1," .. modName .. "); " .. str
return loadstring(str, chunkname)
end
function modEnv.source(filename, env)
if isAbsolutPath(filename) then
source(filename, modName)
else
source(filename)
end
end
function modEnv.InitEventClass(classObject, className)
InitEventClass(classObject, modName .. "." .. className)
end
function modEnv.InitObjectClass(classObject, className)
InitObjectClass(classObject, modName .. "." .. className)
end
function modEnv.registerObjectClassName(object, className)
registerObjectClassName(object, modName .. "." .. className)
end
function modEnv.registerPlaceableType(typeName, object)
registerPlaceableType(modName .. "." .. typeName, object)
end
modEnv.InitStaticEventClass = ""
modEnv.InitStaticObjectClass = ""
modEnv.loadMod = ""
modEnv.loadModDesc = ""
modEnv.loadDlcs = ""
modEnv.loadDlcsFromDirectory = ""
modEnv.loadMods = ""
modEnv.deleteFile = ""
modEnv.deleteFolder = ""
if not isDLCFile then
modEnv.getClassObject = ""
modEnv.getFunction = ""
end
if g_dedicatedServerInfo ~= nil then
function modEnv.setFramerateLimiter()
end
modEnv.g_dedicatedServerMinFrameLimit = g_dedicatedServerMinFrameLimit
modEnv.g_dedicatedServerMaxFrameLimit = g_dedicatedServerMaxFrameLimit
end
local onCreateUtil = {}
onCreateUtil.onCreateFunctions = {}
modEnv.g_onCreateUtil = onCreateUtil
function onCreateUtil.addOnCreateFunction(name, func)
onCreateUtil.onCreateFunctions[name] = func
end
function onCreateUtil.activateOnCreateFunctions()
for name in pairs(modOnCreate) do
modOnCreate[name] = nil
end
for name, func in pairs(onCreateUtil.onCreateFunctions) do
modOnCreate[name] = function(self, id)
func(id)
end
end
end
function onCreateUtil.deactivateOnCreateFunctions()
for name in pairs(modOnCreate) do
modOnCreate[name] = nil
end
end
local i = 0
while true do
local baseName = string.format("modDesc.l10n.text(%d)", i)
local name = getXMLString(xmlFile, baseName .. "#name")
if name == nil then
break
end
local text = getXMLString(xmlFile, baseName .. "." .. g_languageShort)
if text == nil then
text = getXMLString(xmlFile, baseName .. ".en")
if text == nil then
text = getXMLString(xmlFile, baseName .. ".de")
end
end
if text == nil then
print("Warning: No l10n text found for entry '" .. name .. "' in mod '" .. modName .. "'")
elseif modEnv.g_i18n:hasModText(name) then
print("Warning: Duplicate l10n entry '" .. name .. "' in mod '" .. modName .. "'. Ignoring this defintion.")
else
modEnv.g_i18n:setText(name, text)
end
i = i + 1
end
local l10nFilenamePrefix = getXMLString(xmlFile, "modDesc.l10n#filenamePrefix")
if l10nFilenamePrefix ~= nil then
local l10nFilenamePrefixFull = Utils.getFilename(l10nFilenamePrefix, modDir)
local l10nXmlFile
local langs = {
g_languageShort,
"en",
"de"
}
for _, lang in ipairs(langs) do
local l10nFilename = l10nFilenamePrefixFull .. "_" .. lang .. ".xml"
if fileExists(l10nFilename) then
l10nXmlFile = loadXMLFile("TempConfig", l10nFilename)
break
end
end
if l10nXmlFile ~= nil then
local textI = 0
while true do
local key = string.format("l10n.texts.text(%d)", textI)
if not hasXMLProperty(l10nXmlFile, key) then
break
end
local name = getXMLString(l10nXmlFile, key .. "#name")
local text = getXMLString(l10nXmlFile, key .. "#text")
if name ~= nil and text ~= nil then
if modEnv.g_i18n:hasModText(name) then
print("Warning: Duplicate l10n entry '" .. name .. "' in '" .. l10nFilename .. "'. Ignoring this defintion.")
else
modEnv.g_i18n:setText(name, text:gsub("\r\n", "\n"))
end
end
textI = textI + 1
end
delete(l10nXmlFile)
else
print("Warning: No l10n file found for '" .. l10nFilenamePrefix .. "' in mod '" .. modName .. "'")
end
end
local title = Utils.getXMLI18N(xmlFile, "modDesc.title", nil, "", modName)
local desc = Utils.getXMLI18N(xmlFile, "modDesc.description", nil, "", modName)
local iconFilename = Utils.getXMLI18N(xmlFile, "modDesc.iconFilename", nil, "", modName)
if title == "" then
print("Error: Missing title in mod " .. modName)
delete(xmlFile)
return
end
if desc == "" then
print("Error: Missing description in mod " .. modName)
delete(xmlFile)
return
end
local isMultiplayerSupported = Utils.getNoNil(getXMLBool(xmlFile, "modDesc.multiplayer#supported"), false)
if modFileHash == nil then
if isMultiplayerSupported then
print("Warning: Only zip mods are supported in multiplayer. You need to zip the mod " .. modName .. " to use it in multiplayer.")
end
isMultiplayerSupported = false
end
if isMultiplayerSupported and iconFilename == "" then
print("Error: Missing icon filename in mod " .. modName)
delete(xmlFile)
return
end
loadModDescInput(xmlFile, modName)
local i = 0
while true do
local baseName = string.format("modDesc.maps.map(%d)", i)
if not hasXMLProperty(xmlFile, baseName) then
break
end
local mapId = Utils.getNoNil(getXMLString(xmlFile, baseName .. "#id"), "")
local defaultVehiclesXMLFilename = Utils.getNoNil(getXMLString(xmlFile, baseName .. "#defaultVehiclesXMLFilename"), "")
local mapTitle = Utils.getXMLI18N(xmlFile, baseName .. ".title", nil, "", modName)
local mapDesc = Utils.getXMLI18N(xmlFile, baseName .. ".description", nil, "", modName)
local mapClassName = Utils.getNoNil(getXMLString(xmlFile, baseName .. "#className"), "")
local mapFilename = Utils.getNoNil(getXMLString(xmlFile, baseName .. "#filename"), "")
local briefingImagePrefix = Utils.getXMLI18N(xmlFile, baseName .. ".briefingImagePrefix", nil, "", modName)
local briefingTextPrefix = Utils.getXMLI18N(xmlFile, baseName .. ".briefingTextPrefix", nil, "", modName)
local mapIconFilename = Utils.getXMLI18N(xmlFile, baseName .. ".iconFilename", nil, "", modName)
local altMapIconFilename = Utils.getXMLI18N(xmlFile, baseName .. ".altIconFilename", nil, mapIconFilename, modName)
if mapClassName:find("[^%w_]") ~= nil then
print("Error: invalid map class name: " .. mapClassName)
elseif mapId ~= "" and mapTitle ~= "" and mapDesc ~= "" and mapClassName ~= "" and mapFilename ~= "" and defaultVehiclesXMLFilename ~= "" and briefingImagePrefix ~= "" and briefingTextPrefix ~= "" and mapIconFilename ~= "" then
local customEnvironment
local useModDirectory = true
local baseDirectory = modDir
mapFilename, useModDirectory = Utils.getFilename(mapFilename, baseDirectory)
if useModDirectory then
customEnvironment = modName
mapClassName = modName .. "." .. mapClassName
end
mapId = modName .. "." .. mapId
mapIconFilename = Utils.getFilename(mapIconFilename, baseDirectory)
altMapIconFilename = Utils.getFilename(altMapIconFilename, baseDirectory)
briefingImagePrefix = Utils.getFilename(briefingImagePrefix, baseDirectory)
defaultVehiclesXMLFilename = Utils.getFilename(defaultVehiclesXMLFilename, baseDirectory)
MapsUtil.addMapItem(mapId, mapFilename, mapClassName, briefingImagePrefix, briefingTextPrefix, defaultVehiclesXMLFilename, mapTitle, mapDesc, mapIconFilename, altMapIconFilename, baseDirectory, customEnvironment)
end
i = i + 1
end
local version = Utils.getXMLI18N(xmlFile, "modDesc.version", nil, "", modName)
local author = Utils.getXMLI18N(xmlFile, "modDesc.author", nil, "", modName)
if isDLCFile then
local dlcProductId = getXMLString(xmlFile, "modDesc.productId")
if dlcProductId == nil or version == nil then
print("Error: invalid product id or version in DLC " .. modName)
elseif not g_isDemo then
addNotificationFilter(dlcProductId, version)
end
end
iconFilename = Utils.getFilename(iconFilename, modDir)
ModsUtil.addModItem(title, desc, version, author, iconFilename, modName, modDir, modFile, isMultiplayerSupported, modFileHash, absBaseFilename, isDirectory)
delete(xmlFile)
end[/CODE]
[QUOTE=devilkkw;46203655]Interessing....
From my test i've discover thath:
Open i3d with normal shape file:
reading frist 4096 bytes,next 4096 bytes,next 4096.
finally it start reading shape from offeset 8192.
Open i3d with cripted shape file:
reading frist 4096 bytes,reading frist 4096 bytes,reading next 4096 bytes,
finally it start reading shape from offeset 4096.
[...][/QUOTE]
How did you monitor the specific file access?
[QUOTE=Tamschi;46253703]How did you monitor the specific file access?[/QUOTE]
I would guess by looking at the fread() size and count parameters in IDA.
[QUOTE=birkett;46259237]I would guess by looking at the fread() size and count parameters in IDA.[/QUOTE]
You just look at the system calls it uses like ReadFileA QueryFile etc
Sorry to bump this but I'm still interested in making this project a reality. The findings posted here are very interesting but nothing conclusive. As said, I want to attempt to do this myself, I just need some tutoring in how I can approach this. I've seen lots of you facepunchers reversing fileformats, and this can't be such a challenge, can it?
Hi. Did somebody find solution? I have opened dlc from Farming simulator 2015 problem is that i can not open those i3d files. i found in editor log this massage: Error: Shape from 'tatraPhoenix.i3d.shapes' too big (1332681 KB). Maximum supported size is 16384 KB. The tatraPhoenix.i3d.shapes have 3 401 KB. Is somebody interested to find some solution for this please?
I'm not giving up on this project!
I've managed to sneak around in IDA and found this monstrosity of code related to reading the file. It does the rotate/xoring 10 times which leads me to believe this some weird way of encrypting their data? Anybody recognize anything like this?
[code]int __thiscall sub_107AC60(int this, int a2, unsigned int a3, int a4)
{
int v4; // edx@1
int result; // eax@1
int v6; // edi@1
unsigned int i; // ecx@4
int v8; // eax@5
char *v9; // esi@6
char v10; // bl@6
int in1_2; // edi@8
int in5_2; // ecx@8
int in9_2; // edx@8
int in13_2; // esi@8
int v15; // ebx@9
int v16; // ecx@9
int v17; // ebx@9
int v18; // edx@9
int v19; // ebx@9
int v20; // esi@9
int v21; // ebx@9
int v22; // ST60_4@9
int v23; // ebx@9
int v24; // edi@9
int v25; // ebx@9
int v26; // ST74_4@9
int v27; // edi@9
int v28; // ebx@9
int v29; // ST80_4@9
int v30; // edi@9
int v31; // ebx@9
int v32; // ST84_4@9
int v33; // ST70_4@9
int v34; // ebx@9
int v35; // edi@9
int v36; // ebx@9
int v37; // ST7C_4@9
int v38; // edi@9
int v39; // ebx@9
int v40; // ST78_4@9
int v41; // edi@9
int v42; // ST6C_4@9
int v43; // edi@9
int v44; // ST8C_4@9
int v45; // ebx@9
int v46; // edi@9
int v47; // ebx@9
int v48; // ST5C_4@9
int v49; // edi@9
int v50; // ebx@9
int v51; // ST64_4@9
int v52; // edi@9
int v53; // ST68_4@9
int v54; // edi@9
int v55; // ST88_4@9
int v56; // ebx@9
int v57; // edi@9
int v58; // ebx@9
int v59; // edi@9
int v60; // ebx@9
int v61; // edi@9
int v62; // edi@9
int v63; // ebx@9
int v64; // edi@9
int v65; // ebx@9
int v66; // edi@9
int v67; // edi@9
int v68; // edi@9
int v69; // ebx@9
int v70; // edi@9
int v71; // edi@9
int v72; // ebx@9
int v73; // edi@9
int v74; // edi@9
int v75; // edi@9
int v76; // ebx@9
int v77; // edi@9
int v78; // ebx@9
int v79; // edi@9
int v80; // edi@9
bool cont; // zf@9
int v82; // ecx@10
int v83; // ebx@10
int v84; // ecx@10
int v85; // edx@10
int v86; // edx@10
unsigned int v87; // edx@12
int v88; // eax@16
int v89; // [sp+4h] [bp-C8h]@1
int v90; // [sp+8h] [bp-C4h]@0
int in4; // [sp+Ch] [bp-C0h]@2
int in2; // [sp+10h] [bp-BCh]@2
int in8; // [sp+14h] [bp-B8h]@2
int in6; // [sp+18h] [bp-B4h]@2
int in16; // [sp+1Ch] [bp-B0h]@2
int in1; // [sp+20h] [bp-ACh]@2
int in15; // [sp+24h] [bp-A8h]@2
int in7; // [sp+28h] [bp-A4h]@2
int in14; // [sp+2Ch] [bp-A0h]@2
int in5; // [sp+30h] [bp-9Ch]@2
int in13; // [sp+34h] [bp-98h]@2
int in3; // [sp+38h] [bp-94h]@2
int in12; // [sp+3Ch] [bp-90h]@2
int in11; // [sp+40h] [bp-8Ch]@2
int v105; // [sp+44h] [bp-88h]@1
signed int loopcount; // [sp+48h] [bp-84h]@8
int v107; // [sp+48h] [bp-84h]@10
int in9; // [sp+4Ch] [bp-80h]@2
int in10; // [sp+50h] [bp-7Ch]@2
int in4_2; // [sp+54h] [bp-78h]@8
int v111; // [sp+58h] [bp-74h]@9
int v112; // [sp+58h] [bp-74h]@10
int in8_2; // [sp+5Ch] [bp-70h]@8
int v114; // [sp+5Ch] [bp-70h]@10
int in12_2; // [sp+60h] [bp-6Ch]@8
int v116; // [sp+60h] [bp-6Ch]@10
int in7_2; // [sp+64h] [bp-68h]@8
int v118; // [sp+64h] [bp-68h]@10
int in2_2; // [sp+68h] [bp-64h]@8
int v120; // [sp+68h] [bp-64h]@10
int in10_2; // [sp+6Ch] [bp-60h]@8
int v122; // [sp+6Ch] [bp-60h]@10
int in3_2; // [sp+70h] [bp-5Ch]@8
int v124; // [sp+70h] [bp-5Ch]@10
int in15_2; // [sp+74h] [bp-58h]@8
int v126; // [sp+74h] [bp-58h]@10
int in14_2; // [sp+78h] [bp-54h]@8
int v128; // [sp+78h] [bp-54h]@10
int in6_2; // [sp+7Ch] [bp-50h]@8
int v130; // [sp+7Ch] [bp-50h]@10
int in16_2; // [sp+80h] [bp-4Ch]@8
int v132; // [sp+80h] [bp-4Ch]@10
int in11_2; // [sp+84h] [bp-48h]@8
int v134; // [sp+84h] [bp-48h]@10
char v135[64]; // [sp+88h] [bp-44h]@5
v4 = a3;
result = a2;
v6 = a4;
v89 = this;
v105 = a4;
if ( a3 )
{
in1 = *(_DWORD *)this;
in2 = *(_DWORD *)(this + 4);
in3 = *(_DWORD *)(this + 8);
in4 = *(_DWORD *)(this + 12);
in5 = *(_DWORD *)(this + 16);
in6 = *(_DWORD *)(this + 20);
in7 = *(_DWORD *)(this + 24);
in8 = *(_DWORD *)(this + 28);
in9 = *(_DWORD *)(this + 32);
in10 = *(_DWORD *)(this + 36);
in11 = *(_DWORD *)(this + 40);
in12 = *(_DWORD *)(this + 44);
in13 = *(_DWORD *)(this + 48);
in14 = *(_DWORD *)(this + 52);
in15 = *(_DWORD *)(this + 56);
in16 = *(_DWORD *)(this + 60);
while ( 1 )
{
if ( (unsigned int)v4 < 0x40 )
{
i = 0;
if ( v4 )
{
v8 = result - (_DWORD)v135;
do
{
v9 = &v135[i];
v10 = *(&v135[i++] + v8);
*v9 = v10;
}
while ( i < v4 );
}
result = (int)v135;
v90 = v6;
v105 = (int)v135;
}
in1_2 = in1;
in3_2 = in3;
in6_2 = in6;
in10_2 = in10;
in14_2 = in14;
in2_2 = in2;
in7_2 = in7;
in11_2 = in11;
in15_2 = in15;
in4_2 = in4;
in5_2 = in5;
in8_2 = in8;
in9_2 = in9;
in12_2 = in12;
in13_2 = in13;
in16_2 = in16;
loopcount = 10;
do
{
v15 = __ROL4__(in13_2 + in1_2, 7);
v16 = v15 ^ in5_2;
v17 = __ROL4__(v16 + in1_2, 9);
v18 = v17 ^ in9_2;
v19 = __ROL4__(v18 + v16, 13);
v20 = v19 ^ in13_2;
v21 = __ROR4__(v20 + v18, 14);
v22 = v21 ^ in1_2;
v23 = __ROL4__(in6_2 + in2_2, 7);
v24 = v23 ^ in10_2;
v25 = __ROL4__((v23 ^ in10_2) + in6_2, 9);
v26 = v24;
v27 = v25 ^ in14_2;
v28 = __ROL4__((v25 ^ in14_2) + v26, 13);
v29 = v27;
v30 = v28 ^ in2_2;
v31 = __ROR4__((v28 ^ in2_2) + v29, 14);
v32 = v31 ^ in6_2;
v33 = v30;
v34 = __ROL4__(in11_2 + in7_2, 7);
v35 = v34 ^ in15_2;
v36 = __ROL4__((v34 ^ in15_2) + in11_2, 9);
v37 = v35;
v38 = v36 ^ in3_2;
v39 = __ROL4__((v36 ^ in3_2) + v37, 13);
v40 = v38;
v41 = v39 ^ in7_2;
v42 = v41;
v43 = __ROR4__(v40 + v41, 14);
v44 = v43 ^ in11_2;
v45 = __ROL4__(in16_2 + in12_2, 7);
v46 = v45 ^ in4_2;
v47 = (v45 ^ in4_2) + in16_2;
v48 = v46;
v47 = __ROL4__(v47, 9);
v49 = v47 ^ in8_2;
v50 = (v47 ^ in8_2) + v48;
v51 = v49;
v50 = __ROL4__(v50, 13);
v52 = v50 ^ in12_2;
v53 = v52;
v54 = __ROR4__(v51 + v52, 14);
v55 = v54 ^ in16_2;
v56 = __ROL4__(v48 + v22, 7);
v57 = v56 ^ v33;
v58 = (v56 ^ v33) + v22;
in2_2 = v57;
v58 = __ROL4__(v58, 9);
v59 = v58 ^ v40;
v60 = (v58 ^ v40) + in2_2;
in3_2 = v59;
v60 = __ROL4__(v60, 13);
v61 = v60 ^ v48;
in4_2 = v61;
v62 = __ROR4__(in3_2 + v61, 14);
v111 = v62 ^ v22;
v63 = __ROL4__(v32 + v16, 7);
v64 = v63 ^ v42;
v65 = (v63 ^ v42) + v32;
in7_2 = v64;
v65 = __ROL4__(v65, 9);
v66 = v65 ^ v51;
in8_2 = v66;
v67 = __ROL4__(in7_2 + v66, 13);
in5_2 = v67 ^ v16;
v68 = __ROR4__(in5_2 + in8_2, 14);
in6_2 = v68 ^ v32;
v69 = __ROL4__(v44 + v26, 7);
v70 = v69 ^ v53;
in12_2 = v70;
v71 = __ROL4__(v44 + v70, 9);
in9_2 = v71 ^ v18;
v72 = __ROL4__(in12_2 + in9_2, 13);
v73 = v72 ^ v26;
in10_2 = v73;
v74 = __ROR4__(in9_2 + v73, 14);
in11_2 = v74 ^ v44;
v75 = __ROL4__(v37 + v55, 7);
in13_2 = v75 ^ v20;
v76 = __ROL4__(v55 + in13_2, 9);
v77 = v76 ^ v29;
v78 = (v76 ^ v29) + in13_2;
in14_2 = v77;
v78 = __ROL4__(v78, 13);
v79 = v78 ^ v37;
in15_2 = v79;
v80 = __ROR4__(in14_2 + v79, 14);
in16_2 = v80 ^ v55;
cont = loopcount-- == 1;
in1_2 = v111;
}
while ( !cont );
v107 = *(_DWORD *)(result + 16) ^ (in5 + in5_2);
v130 = *(_DWORD *)(result + 20) ^ (in6 + in6_2);
v118 = *(_DWORD *)(result + 24) ^ (in7 + in7_2);
v82 = in8_2;
v114 = *(_DWORD *)(result + 32) ^ (in9 + in9_2);
v122 = *(_DWORD *)(result + 36) ^ (in10 + in10_2);
v83 = *(_DWORD *)(result + 12) ^ (in4_2 + in4);
v84 = *(_DWORD *)(result + 28) ^ (in8 + v82);
v134 = *(_DWORD *)(result + 40) ^ (in11 + in11_2);
v85 = in12_2;
v116 = *(_DWORD *)(result + 48) ^ (in13 + in13_2);
v86 = *(_DWORD *)(result + 44) ^ (in12 + v85);
v128 = *(_DWORD *)(result + 52) ^ (in14 + in14_2);
v112 = *(_DWORD *)result ^ (in1 + v111);
v126 = *(_DWORD *)(result + 56) ^ (in15 + in15_2);
v120 = *(_DWORD *)(result + 4) ^ (in2 + in2_2);
v132 = *(_DWORD *)(result + 60) ^ (in16 + in16_2);
cont = in9++ == -1;
v124 = *(_DWORD *)(result + 8) ^ (in3 + in3_2);
if ( cont )
++in10;
*(_DWORD *)v105 = v112;
*(_DWORD *)(v105 + 28) = v84;
*(_DWORD *)(v105 + 4) = v120;
*(_DWORD *)(v105 + 32) = v114;
*(_DWORD *)(v105 + 44) = v86;
*(_DWORD *)(v105 + 8) = v124;
*(_DWORD *)(v105 + 36) = v122;
*(_DWORD *)(v105 + 48) = v116;
*(_DWORD *)(v105 + 16) = v107;
*(_DWORD *)(v105 + 40) = v134;
*(_DWORD *)(v105 + 56) = v126;
v87 = a3;
*(_DWORD *)(v105 + 20) = v130;
*(_DWORD *)(v105 + 52) = v128;
*(_DWORD *)(v105 + 12) = v83;
*(_DWORD *)(v105 + 24) = v118;
*(_DWORD *)(v105 + 60) = v132;
if ( a3 <= 0x40 )
break;
v4 = a3 - 64;
result += 64;
v6 = v105 + 64;
a3 -= 64;
v105 += 64;
}
if ( a3 < 0x40 && a3 )
{
v88 = v90;
do
{
*(_BYTE *)v88 = *(_BYTE *)(v105 - v90 + v88);
++v88;
--v87;
}
while ( v87 );
}
result = v89;
*(_DWORD *)(v89 + 32) = in9;
*(_DWORD *)(v89 + 36) = in10;
}
return result;
}[/code]
[QUOTE=Donkie;50609563]I'm not giving up on this project!
I've managed to sneak around in IDA and found this monstrosity of code related to reading the file. It does the rotate/xoring 10 times which leads me to believe this some weird way of encrypting their data? Anybody recognize anything like this?
[/QUOTE]
If they're xoring it 10 times then it's almost definitely homebrew encryption. Figure out that key and you've got the files.
fwiw, here is C# code to decrypt the .i3d.shape files. Note that the decrypted data is in some binary format.
[url=http://pastebin.com/kzwt6qG3]Full Source here[/url]
[code] public void DecryptBlocks(uint[] buf)
{
if (buf.Length % 0x10 != 0)
throw new Exception("Expecting 16 byte blocks");
var tempKey = new uint[m_Key.Length];
ulong blockCounter = m_Key[8] | (m_Key[9] << 32);
for (var i = 0; i < buf.Length; i += 0x10)
{
m_Key.CopyTo(tempKey, 0);
for (int j = 0; j < 10; j++)
{
Shuffle1(tempKey, 0x0, 0xC, 0x4, 0x8);
Shuffle1(tempKey, 0x5, 0x1, 0x9, 0xD);
Shuffle1(tempKey, 0xA, 0x6, 0xE, 0x2);
Shuffle1(tempKey, 0xF, 0xB, 0x3, 0x7);
Shuffle2(tempKey, 0x3, 0x0, 0x1, 0x2);
Shuffle2(tempKey, 0x4, 0x5, 0x6, 0x7);
Shuffle1(tempKey, 0xA, 0x9, 0xB, 0x8);
Shuffle2(tempKey, 0xE, 0xF, 0xC, 0xD);
}
for(var j = 0; j < m_Key.Length; j++)
buf[i + j] ^= m_Key[j] + tempKey[j];
blockCounter++;
m_Key[8] = (uint)(blockCounter & 0xFFFFFFFF);
m_Key[9] = (uint)(blockCounter >> 32);
}
}[/code]
[img]http://i.imgur.com/JzCjIum.png[/img]
Haha, I solved it just today actually. Your code is way more cleaned up though. Thanks for the help!
[QUOTE=high;50648671]fwiw, here is C# code to decrypt the .i3d.shape files. Note that the decrypted data is in some binary format.
[url=http://pastebin.com/kzwt6qG3]Full Source here[/url]
[code][...][/code]
[img]http://i.imgur.com/JzCjIum.png[/img][/QUOTE]
That looks like a fairly standard graphics buffer dump, with the index buffer being in view here.
[QUOTE=Donkie;50648940]Haha, I solved it just today actually. Your code is way more cleaned up though. Thanks for the help![/QUOTE]
You probably only have to figure out where the buffer segments start.
For large meshes with skeletal animation, there may be multiple draw calls that map bone indices differently (since the amount of full-size matrix parameters you can pass to a shader at once is fairly limited).
How can i run this script ?
For anyone stumbling upon this thread, here is a tool I made that works with the findings of this thread: GitHub
It has a few dependencies and you have to compile it yourself, but it outputs in a variety of 3D formats.
Sorry, you need to Log In to post a reply to this thread.