[GUIDE] [1.20.0.7] Editing game files using a hex editor

Forum rules
SCS as a company do not wish to have paid mods on this forum. While we understand that not all paid mods use the Intellectual Property of other companies/people, it is very hard to moderate what is and isn't acceptable when money is involved. There are also concerns that it could look unfavorable to potential work partners going forward if SCS allow mods that may potentially use unlicensed branding.
Posting in the Mods forum (ATS and ETS2) is restricted to sharing free-to-the-public mods and providing support for mods. For more details, please check chapters [4] and [5] of Forum Rules.
User avatar
Cadde
Posts: 9535
Joined: 24 Apr 2013 18:08
Location: Have no fear, i am from the internets!

[GUIDE] [1.20.0.7] Editing game files using a hex editor

#1 Post by Cadde » 10 Sep 2015 12:12

This thread is intended as a community project thread. That means, all moderators and admins are free to edit this and any reserved posts if new information arises from the community.
The thread/post might start off very basic and eventually grow to cover all aspects of the subject.

Note, this guide WILL be highly technical and advanced to users not familiar with binary formats and hex editors.
While i will do my utmost to keep it as user friendly as possible. I will not resort to hand holding in this guide.
If you don't understand something here fully i highly advise you to educate yourself on that using a combination of Google and asking questions before you venture into actually changing something and saving/using it.


This guide assumes you have extracted the game archives and have a powerful hex editor installed.
The basic guide to extract the game files can be found here: viewtopic.php?f=172&t=189533
A powerful hex editor, 010 Editor, can be found here (free trial): http://www.sweetscape.com/download/

I will not blame you if you extend that trial period, not even when you do it indefinitely, through a simple registry edit. But i will not go into further details on that. I just wanted to let you know that those 30 days can be extended that's all.

This guide will be written with 010 Editor used as the editor of choice. All code/scripts/templates are based on 010 Editor and as such i highly recommend you download a copy or you will not be able to follow along entirely.

Also, i would like to apologize for the large resolution of my screen in advance.

This guide was written for the game version that can be seen in the threads title/subject, but generally should apply to later versions of the game as well unless specifically changed.
You can find out what's been changed by checking the respective patch guides here: http://eurotrucksimulator2.com/mod_tools.php

------------------------------ END HEADER ------------------------------

Editing Game Files Using A Hex Editor.

Image
Just setting the mood...

So you want to be a haxxor? Well, no... You want to be able to change things at their core rather than through some heavy, clunk, difficult to use program. Right?
In most people's minds (at least those i have met) a hex editor is as much black magic as Chinese is to someone who doesn't speak it. Most people associate hex editors with the dark arts.
But that isn't the case, not at all.

Everything looks daunting when you first look at it and you have no prior experience with what you see.

Image
Three TOBJ files open in 010 Editor. (Click for full size)
 
 
 
Familiarizing yourself with the core concepts.

Let's do a crash course in digitally stored data before we move on. Starting with some of the basic data types.

Code: Select all

Names                       Bits    Value range                         Num values          Comment
------------------------------------------------------------------------------------------------------------------------------------------
char, byte                  8       -128 to +127                        256                 Signed Byte or character
uchar, ubyte                8       0 to 255                            256                 Unsigned Byte or character.
short, int16                16      −32,768 to +32,767                  65,536              Signed 16 bit integer
ushort, uint16, WORD        16      0 to 65535                          65,536              Unsigned 16 bit integer
int, int32, long            32      −2,147,483,648 to +2,147,483,647    4,294,967,295       Signed 32 bit integer. (Most common for reasons) *
uint, uint32, ulong, DWORD  32      0 to 4,294,967,295                  4,294,967,295       Unsigned 32 bit integer. (Also common)
int64, quad, __int64        64      -4,294,967,296 to 4,294,967,295     1.8446744e+19       Signed 64 bit integer.
uint64, uquad, QWORD        64      0 to 1.8446744e+19                  1.8446744e+19       Unsigned 64 bit integer.

hfloat                      16      https://en.wikipedia.org/wiki/Floating_point            Half precision floating point number.
float                       32      https://en.wikipedia.org/wiki/Floating_point            Floating point number.
double                      64      https://en.wikipedia.org/wiki/Floating_point            Double precision floating point number.

string                      --      *Technically* Infinity                                  An array of char. **
wstring                     --      *Technically* Infinity                                  An array of wchar. ***

* - 32 bit integers are common because they fit neatly into memory chunks. Without getting too technical, if your data structure fits neatly into 4 byte
    aligned chunks it's faster to access the data than if you had a mix of bytes, shorts and ints or quads.
    
** - To make a string you generally say "everything until the next null (0x00) byte or char" or alternatively "N chars" where N is a value read before the string.
     Strings usually come LAST in a file for the alignment reason stated above but they don't have to and if they come before such numerical data as above they are
     usually padded with null (0x00) bytes to make the numerical data align neatly but even that isn't necessary. It's just that programmers and those designing file
     formats are usually very OCD about these things. ;)
     
     Strings can be length prefixed or their length is stored somewhere else.
     Strings that are length prefixed ALWAYS have the length of the string as a value right before it. The value can be in byte, short or int form.
     Yes, technically the length prefix can also be in quad form but who the F uses strings longer than 4.2 billion characters?

*** - wchar is two chars in a pair. Read about unicode if you want to know more: https://en.wikipedia.org/wiki/Unicode
      Not commonly used in ETS 2.
These data types are specific to 010 editor but are generally globally compatible. What good is a hex editor if it would make stuff up?
So if you have a programming language with data types, you should be able to copy-paste those type definitions straight into your hex editor (with minor editing) to read the data.
And the other way around holds true too. You could technically copy the data types from the hex editor to a programming language of choice that uses data types and use them in code.
We will revisit this later on when we start looking at templates and scripts.

Another thing you should be aware of is the difference between different bases. In this case base 2 (binary), base 10 (decimal) and base 16 (hexadecimal)
Hex (hexadecimal) editors generally show the contents of files bytes using hexadecimal numbers. As each byte is in the range 0-255 it's a perfect opportunity to use Hexadecimal.
Hexadecimal (as you should know, if not click here) ranges from 0, 1, 2, ..., 9, A, B, C, D, E, F.
It's as if you kept counting after 9 using the alphabet, only counting back at zero after you counted to the letter F.

So, instead of counting 1, 2, 3, ..., 8, 9, 10...
We count 1, 2, 3, ..., D, E, F, 10!
10 in hexadecimal is 16 in decimal. Got it? Cool!

How do we make the distinction between decimal and hexadecimal notation? It would get very confusing if i put 10 and actually meant 16, right?
There are many different ways. For the sake of simplicity i will ALWAYS use "0x" to represent hexadecimal numbers. If you want to know other ways of representing hex numbers then look them up!

Code: Select all

0x10 = 16       (1 * 16 + 0)
0x80 = 128      (8 * 16 + 0)
0xFF = 255      (15 * 16 + 15)

0x42 = 66       (4 * 16 + 2)

And for the heck of it...
0x1337 = 4919   (1 * 4096 + 3 * 256 + 3 * 16 + 7
Instead of taking up three character slots when counting numbers over 99 in decimal. Hexadecimal only ever needs two!

With this newfound knowledge (assuming you didn't have it before that is) take a look at the previous screenshot again.
Can you tell what part of the files are numerical data and what parts of the files are strings? It's always a good idea when looking at new files to make the distinction between what makes up a string and what makes up binary data.
 
 
 
A brief introduction to reverse engineering file formats.

This is the process i tend to follow when trying to figure out a file format, distinguishing strings from binary data.
To help you make this distinction, i have highlighted the string in green and the binary data in red.

Image
Numerical data vs string data.

If you want to follow along here, open the the file /vehicle/truck/upgrade/paintjob/paintjob_010.tobj from the game extracts. Doing so might make it easier for you to understand the process.
Then select the bytes highlighted and press CTRL+B (add bookmark), select the background (and foreground) color(s) and give it a descriptive name.

Moving on, as you can see the string here is very telling. It's the path of the DDS texture file that this TOBJ file is describing. This path is a Linux style path using forward slashes and it assumes the game/zip/scs archive is the "root" directory.
Hence, if this was inside a mod archive, the path is relevant only to the file structure inside the mod archive.
Thus, with this knowledge we KNOW what the string is most likely used for. That's why i highlighted it in a green tone to classify it as KNOWN.

One thing you should have noticed here too is, the string doesn't have a terminating null byte. That is, the string ends in 2E 64 64 73 or ".dds" and doesn't have any more bytes after it.
From the data types above, you should now know that strings are either null terminated or length prefixed... somewhere. And in this case we can assume the string is length prefixed due to a lacking null terminator.
This simplifies reverse engineering the binary data somewhat, because we can find this value and hence also mark it known when we do.

To start, we should find out how long the string is... There are at least 2 user friendly ways to do this.

1) Select the bytes of the string and look in the lower right corner of your editor.
Image
Measure string length by selecting it.

2) Alternatively, since we bookmarked it, check your bookmarks.
Image
Or check the size of your bookmark to see how long the string is.

Personally, i prefer the first way in this case because you get (at a glance) both the size in base 10 (decimal) and base 16 (hexadecimal) notation.
So the string is 48 (0x30) characters long.

Let's search for the length value in the data!
Remember what i said about length prefixed strings in that code segment above? Strings are either length prefixed, with their length right before the string. Or the length is defined elsewhere.
Since we can clearly tell the 4 bytes before the string are all null (0x00) bytes we can safely assume the string isn't length prefixed and the value is stored elsewhere.

So since the string isn't length prefixed... Remember what i said about 4 byte alignment? With this knowledge we should always assume the length of the string is defined as a 32 bit (4 byte wide) integer first. Only when that fails do we look for other value sizes.
Alas, let's search for a 32 bit integer with the value '48'. Press CTRL+F to search for data.

Image
Search for data...

Even though we could use a signed integer here. Select Unsigned Int because we won't be looking for strings with a negative size anyways.
Type in the value we are looking for and press the text that reads All

You should then get this...

Image
... and you shall find!

But how can we be sure that what we found really is the length of the string? It could just be coincidence, right?
Even so, sometimes you find more than one result... How can we tell the results apart?

Easy! We just repeat the process on many different TOBJ files!

Image
Verifying what you have found.

Cool huh?
We can now bookmark that too because we are certain that we have found what we were looking for. But to do so, we need to remove the old red bookmark as you cannot place a bookmark on top of another bookmark.

Image
Bookmarked path string length.

So what about the other data? How do we know what that does?
Generally, without a lot of researching you don't! So in the interest of not having you read a whole book on every subject there is to know about textures, 3D modeling and so on, let me just tell you what i know. Which isn't a lot to be honest because i don't care about the other data.

Image
A template with coloring

Let me explain...

Green = Version number of the TOBJ file. This stays the same on all TOBJ files i have come across.
Red = Unknown data that never changes and is always null bytes, hence i cannot decipher it even if i wanted to.
Orange = Data that changes depending on several factors. Which i will get to later.
Blue = Make a guess...
Black = The path of the dds file this TOBJ file describes.

As you can tell, everything in the data (which from this point on we can call a header, because that's what it is) neatly aligns on 4 byte boundaries.
Hence it's probably safe to assume that all data in the header is stored in 4 byte chunks.

Now, let me explain the data marked in orange. I don't actually know what it does but i know it matters a lot to how the texture is displayed and used in game.
However to explain this i must first make a brief introduction to bit flags.

Remember that i talked about base 2 as the third alternative besides base 10 and base 16?
An integer stored in a binary format is actually stored as a sequence of bits. Just like counting in decimal where we add 1 to the left once we reached number 9 and just like hexadecimal where we add 1 to the left when we reach number F. In binary we add 1 to the left when we reach 1!

Hence, to count to ten in binary...

Code: Select all

Decimal     Binary
00          0000
01          0001
02          0010
03          0011
04          0100
05          0101
06          0110
07          0111
08          1000
09          1001
10          1010
Each bit is either ON or OFF, 1 or 0. This can be compared to storing up to 8 on/off switches per byte.
Imagine you had 8 different machines all doing various tasks. You have 8 switches to turn each one of them on and off... well it's the same thing here!
The game possibly has up to 160 different "machines" (settings) to toggle between on textures.

Some of these toggles are known by name thanks to the Wiki guide on SCS Blender tools.
Settings of TOBJ file are:

U Repeat - repeat texture in U direction.
V Repeat - repeat texture in V direction.
TS Normal - tangent space normal for the texture.
No MIP Maps - don't use MIP Maps for the texture.
No Compress - don't use compression on texture.
But i haven't taken the time to map those settings to various flags in the TOBJ file format and i will explain why later.
If we lay out the binary data of all bytes marked in orange shown in the last picture we get this...

Code: Select all

00000001 00000000 00000010 00000000
00000010 00000000 00000011 00000011
00000011 00000000 00000010 00000010
00000000 00000001 00000000 00000000
00000000 00000001 00000000 00000000
So again, think of this as an array of switches that are either on or off on a huge control panel.

Image
 
 
A switchboard with on/off switches

I must put emphasis on 'might be' here. Again, i don't know because i haven't checked.
All i know is, the settings must be right for the model and texture used. Hence i ALWAYS copy an existing TOBJ file for the type of texture i am going to work with and only change the path.
It is a very common mistake among modders who use TOBJ files not suited for the texture they put in and then experience weird glitches and possibly even crashes. Especially in OpenGL mode.
The values DO change and they ARE important so make sure you use the right TOBJ file. And again, to know that you should copy an existing file that corresponds to what you are working on.

Of course, any of these 4 byte groups might actually be 32 bit integers too! Just FYI.

And you don't really have to worry about the things you don't know about. If you goal is to change the path inside a TOBJ file then you have already accomplished this goal! Just change the path and make sure the string size integer is updated, save the file and off you go!
 
 
 
Using Templates in 010 Editor

Templates and scripts are very powerful features of 010 Editor. They allow you to organize the data in a file after the specified value types.
For example, here's a template that is usable in 010 editor for TOBJ files:

Code: Select all

//--------------------------------------
//--- 010 Editor v5.0 Binary Template
//
// File: TOBJ.bt
// Author: Cadde
// Revision: 1.1
// Purpose: For ETS 2 TOBJ files.
//--------------------------------------

typedef struct {
    uint32 version <bgcolor=cDkGreen>;
    uint32 un2 <fgcolor=cRed, bgcolor=cDkRed, hidden=true>;
    uint32 un3 <fgcolor=cRed, bgcolor=cDkRed, hidden=true>;
    uint32 un4 <fgcolor=cRed, bgcolor=cDkRed, hidden=true>;
    uint32 un5 <fgcolor=cRed, bgcolor=cDkRed, hidden=true>;
    uint32 un6 <fgcolor=cBlack, bgcolor=0x0080FF>;
    uint32 un7 <fgcolor=cBlack, bgcolor=0x0080FF>;
    uint32 un8 <fgcolor=cBlack, bgcolor=0x0080FF>;
    uint32 un9 <fgcolor=cBlack, bgcolor=0x0080FF>;
    uint32 un10 <fgcolor=cBlack, bgcolor=0x0080FF>;
    uint32 fileNameLength <fgcolor=cWhite, bgcolor=cBlue>;
    uint32 un11 <fgcolor=cRed, bgcolor=cDkRed, hidden=true>;
    if (fileNameLength > 0)
        char path[fileNameLength] <fgcolor=cWhite, bgcolor=cBlack>;
} TOBJ <read = TOBJRead, write = TOBJWrite>;

string TOBJRead( TOBJ &tobj ) {
    if (exists(tobj.path))
        return tobj.path;
    else
        return "";
}

void TOBJWrite( TOBJ &tobj, string s ) {
    local uint fileNameOffset = FileSize() - tobj.fileNameLength;
    DeleteBytes(fileNameOffset, tobj.fileNameLength);
    WriteBytes(s, fileNameOffset, Strlen(s));
    tobj.fileNameLength = Strlen(s);
    FileSave();
    RunTemplate();
}

TOBJ tobj;
Don't be scared, i will explain it!

Lines starting with double forward slashes '//' are comments. They are ignored by the template engine and let us describe the template.

Code: Select all

typedef struct {
...
} NAME <options>
This defines a structure of various data types. A header if you will, because like i mentioned before, blocks like these usually come before the payload data.
Compare headers to the name, address, zip code, country of a parcel/box with some extra details about the contents of the box.

Code: Select all

Name: Some Guy
Address: Some street 69
Zip: 1234 56 Some city
Country: Zimbabwe

Weight: 12.3 kg
Contents: 123 pcs of chocolate bars
Handling: Must be stored at temperatures below 20 degrees C.
Such a box usually have physical limits on what can fit inside it so there's no need to write the dimensions of the box. But in the digital world, applications that work with a box contents need to know the size of the payload. Like the length of our DDS path in the TOBJ file. The application (or game as it may be) also needs to know various settings (handling instructions) for the data, just like i touched on before with the switchboard.

So, the struct one defines is usually a header and contains the fields the application or "machinery" needs to handle the data (payload) that comes after it.
In the digital world, most things are boxed not just once but several times over. You put a box of data inside another box of data.
For example, your mod archive is a rack of boxes (files) that in turn has their own boxes of data specified using various kinds of structures.

In the case of our TOBJ file, this structure is rather simple. Consisting of...

Code: Select all

Version as a 32 bit unsigned integer.
Nine unknown instances of 32 bit unsigned integers.
FileNameLength as a 32 bit unsigned integer.
One unknown instance of a 32 bit unsigned integer.
At this point you may wonder why i am including the payload (the dds path string) in the "header", it is for the sake of template/script simplicity and while i technically could put it outside the structure definition. I will get to the reason for it being inside later.
I also want to add that the stuff at the end between '<' and '>' tags are optional settings for each field which decorates the data in the hex editor with colors and hides the fields from the template results that i don't care about since they will always be unknown to me.

Then follows two functions...

Code: Select all

string TOBJRead( TOBJ &tobj ) {
    if (exists(tobj.path))
        return tobj.path;
    else
        return "";
}
This is a function. It takes a parameter as it's input, works with it and returns some output. The same way a mathematical function takes one or more parameters as it's input(s) and generates an output.

The '&tobj' parameter is a reference to what has been loaded before (i will get to that too) and is the type of structure we defined above. We can access the various named fields of that structure as you can see in the code snippet.
Basically, the function checks if 'tobj.path' has been set and returns the value of that if it is set. If it isn't set it returns an empty string.

The function is called when the above structure is being read. (as the structure options tells it to)

The read function has a corresponding write function too!

Code: Select all

void TOBJWrite( TOBJ &tobj, string s ) {
    local uint fileNameOffset = FileSize() - tobj.fileNameLength;
    DeleteBytes(fileNameOffset, tobj.fileNameLength);
    WriteBytes(s, fileNameOffset, Strlen(s));
    tobj.fileNameLength = Strlen(s);
    FileSave();
    RunTemplate();
}
It works much the same as the read function but now takes an additional string argument named 's'.

Then it works with that 's' argument to reconstruct the TOBJ payload... First we determine the position in the file where the DDS file path starts. This is simple subtractive math.

Code: Select all

FileSize = 96
FileNameLength = 48 (but can change as you know)
FileNameOffset = FileSize - FileNameLength
48 = 96 - 48
The 'local' before the data type and variable name of 'fileNameOffset' means the variable is LOCAL to the function and not accessible from elsewhere. Without the local keyword, the function would attempt to read the data from the file instead which would break the function.

After that we call 'DeleteBytes(offset, numBytesToDelete)' which removes all the data that the DDS path makes up in our file.
And then we call 'WriteBytes(buffer, offset, numBytesToWrite)' which adds the contents of the string 's' to the file. As i said before, strings are arrays of chars and chars are actually bytes. Hence we write bytes back into the file and those bytes are stored in the 's' input parameter of the function.

When those bytes have been written we change the 'fileNameLength' field of our referenced TOBJ structure to 'Strlen(s)' which in of itself is a function that counts the number of characters there are in the input string 's'.

To wrap it all up, we save the file with the 'FileSave()' call and run the template on the file again with 'RunTemplate()'.
The reason for this is that when you change the contents of a file via a template, 010 Editor doesn't seem to recognize the change or at the very least ignores it. Pressing File->Save doesn't save the file because the editor doesn't know the file has been changed. I don't know if this is intended behavior of 010 editor or if it's a bug. I don't care, i simply run that function and it writes the file because why else would you want to modify the file in the first place?
And i cause a re-run on the template because otherwise the user would have to do so manually.

And finally, there's a single "mysterious" line...

Code: Select all

TOBJ tobj;
This tells the template to read (from the current position of the file, which is zero, at the very beginning) a TOBJ structure and store it in the variable tobj.
Which gives us this!

Image
A template with coloring

You might be asking "will i have to create my own templates now?"
No, you don't have to. I am merely showing you that you CAN if you have the energy to learn basic C programming skills or you already know how. There's extensive documentation on the subject on the 010 Editor webpage.
In any other case, i will provide templates for common SCS Software file formats to the best of my knowledge when asked. But not all formats are as simple as the TOBJ format to work with.
For now, you can use the template i have posted above in full. Simply click "Templates -> New template" and paste the code i gave you above into the .bt (Binary Template) file. Save it as "TOBJ.bt" and you will be able to use it.
 
 
 
Using the TOBJ template from above

Right, you have loaded the above template into your hex editor and you have pretty colors... "What do i do now?"
Double click here...

Image

Type in the new path you want to use and press ENTER / RETURN and the templates TOBJWrite function takes care of the rest for you!
And this is where the whole procedure links in with why the file name is part of the "header" structure. The write function is called when you edit the value of the TOBJ structure as shown above. It passes a reference of the TOBJ structure to the write function and the 's' parameters is the contents of the text box you change after you double click the value.

To make it work in any other way i would have to provide a cumbersome script for this editing capability which would only make it harder to edit TOBJ files for no good reason.
And that is why the path is part of the "header" structure and how the reference to the header structure is passed to the write function.

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

That is it for the guide for now, i will expand on the guide based on feedback from the readers.

No feedback? No more guide contents!

Please see the following posts for more templates / scripts.
On extended hiatus.

User avatar
Cadde
Posts: 9535
Joined: 24 Apr 2013 18:08
Location: Have no fear, i am from the internets!

Re: [GUIDE] [1.20.0.7] Editing game files using a hex editor

#2 Post by Cadde » 10 Sep 2015 12:12

Other templates for formats are on their way, let me just finish editing this post.

All templates/scripts here only work in 010 Editor.

TOBJ File template

TOBJ files are a link between material (.mat) files and the DDS texture files.
They contain some extra options for the textures that are specific to the model (PMD/PMG), material (MAT) and texture (DDS) file chain.
All the files are linked together and only work properly when that chain is maintained.

Hence, if you are making a standalone model (truck/trailer/whatever) then you should also make sure that you copy the materials, tobj files and texture files and make the necessary changes.

Working with TOBJ files is a necessity if you want to make truck skins manually without the use of 3rd party applications like ETS 2 Studio and/or TOBJ edit. (The latter which doesn't work correctly at all mind you)

TOBJ.bt

Code: Select all

//--------------------------------------
//--- 010 Editor v5.0 Binary Template
//
// File: TOBJ.bt
// Author: Cadde
// Revision: 1.1
// Purpose: For ETS 2 TOBJ files.
//--------------------------------------

typedef struct {
    uint32 version <bgcolor=cDkGreen>;
    uint32 un2 <fgcolor=cRed, bgcolor=cDkRed, hidden=true>;
    uint32 un3 <fgcolor=cRed, bgcolor=cDkRed, hidden=true>;
    uint32 un4 <fgcolor=cRed, bgcolor=cDkRed, hidden=true>;
    uint32 un5 <fgcolor=cRed, bgcolor=cDkRed, hidden=true>;
    uint32 un6 <fgcolor=cBlack, bgcolor=0x0080FF>;
    uint32 un7 <fgcolor=cBlack, bgcolor=0x0080FF>;
    uint32 un8 <fgcolor=cBlack, bgcolor=0x0080FF>;
    uint32 un9 <fgcolor=cBlack, bgcolor=0x0080FF>;
    uint32 un10 <fgcolor=cBlack, bgcolor=0x0080FF>;
    uint32 fileNameLength <fgcolor=cWhite, bgcolor=cBlue>;
    uint32 un11 <fgcolor=cRed, bgcolor=cDkRed, hidden=true>;
    if (fileNameLength > 0)
        char path[fileNameLength] <fgcolor=cWhite, bgcolor=cBlack>;
} TOBJ <read = TOBJRead, write = TOBJWrite>;

string TOBJRead( TOBJ &tobj ) {
    if (exists(tobj.path))
        return tobj.path;
    else
        return "";
}

void TOBJWrite( TOBJ &tobj, string s ) {
    local uint fileNameOffset = FileSize() - tobj.fileNameLength;
    DeleteBytes(fileNameOffset, tobj.fileNameLength);
    WriteBytes(s, fileNameOffset, Strlen(s));
    tobj.fileNameLength = Strlen(s);
    FileSave();
    RunTemplate();
}

TOBJ tobj;
Image
Output

To use it, simply double click the value of the TOBJ structure in the Template Results window of 010 editor.

PMD File template (NOT FINISHED)

PMD files are model definition files, they are always paired with a PMG (Model Geometry) file and contains all the information about the models materials, number of looks, number of variants, etc.
The one thing most users want to alter in a PMD file are material paths. Which is the goal of this template.

Code: Select all

//--------------------------------------
//--- 010 Editor v5.0 Binary Template
//
// File: PMD.bt
// Author: Cadde
// Revision: 0.1 Alpha
// Purpose: Work with PMD files from ETS 2.
// Source: Format details shamelessly snagged from Blender2SCS by 50Keda. (http://blender2scs.tk/)
//--------------------------------------

struct PMDHeader {
    uint32 idOrVersion <bgcolor=cYellow>;
    uint32 noMaterials <bgcolor=cDkGreen>;
    uint32 noLooks <bgcolor=cDkGreen>;
    uint32 noParts <hidden=true>;
    uint32 noVariants <bgcolor=cDkGreen>;
    uint32 noVisibLinks <hidden=true>;
    uint32 noVisibDefs <hidden=true>;
    uint32 noVariantsVisib <hidden=true>;
    uint32 materialBlockSize <bgcolor=cDkGreen>;
    uint32 offsetLooks <hidden=true>;
    uint32 offsetVariants <hidden=true>;
    uint32 offsetVisibLinks <hidden=true>;
    uint32 offsetVariantsVisib <hidden=true>;
    uint32 offsetVisibDefs <hidden=true>;
    uint32 offsetMatDefs <hidden=true>;
    uint32 offsetMatBlock <hidden=true>;
};

struct MaterialPath {
    string materialPath;
};

PMDHeader header;
FSeek(header.offsetMatBlock);
local int i = 0;
for (i; i < header.noMaterials * header.noLooks * header.noVariants; i++)
    MaterialPath materials <bgcolor=cBlack, fgcolor=cWhite>;
Image
Output

This template is not meant to be user friendly. You have to manually edit the paths and update the materialBlockSize when you are done.
On extended hiatus.

User avatar
Cadde
Posts: 9535
Joined: 24 Apr 2013 18:08
Location: Have no fear, i am from the internets!

Re: [GUIDE] [1.20.0.7] Editing game files using a hex editor

#3 Post by Cadde » 10 Sep 2015 12:12

Reserved
On extended hiatus.

User avatar
Cadde
Posts: 9535
Joined: 24 Apr 2013 18:08
Location: Have no fear, i am from the internets!

Re: [GUIDE] [1.20.0.7] Editing game files using a hex editor

#4 Post by Cadde » 10 Sep 2015 12:12

That's it for now on using hex editors to edit ETS 2 game binary files.

Please, as per usual, provide feedback on this guide. Give praise or critique and suggest changes to the guide.
Ask questions (i love questions) and in general show that you care about these guides.

The guides do take a long time to finish and i could be doing other things like eating ice cream or playing the game. So please show that you actually want these guides to keep coming and be updated.
On extended hiatus.

User avatar
Pumizo
Beta tester
Posts: 2102
Joined: 11 Jun 2013 18:44
Location: Lisbon - Portugal
Contact:

Re: [GUIDE] [1.20.0.7] Editing game files using a hex editor

#5 Post by Pumizo » 10 Sep 2015 12:47

Amazing topic Cadde. I'm a basic HEX editing user, I mostly used it on Haulin and ETS1 times when we needed to fix the glass reflections using a HEX editor. This topic will be handy for sure!
ImageImageImage

User avatar
Cadde
Posts: 9535
Joined: 24 Apr 2013 18:08
Location: Have no fear, i am from the internets!

Re: [GUIDE] [1.20.0.7] Editing game files using a hex editor

#6 Post by Cadde » 11 Sep 2015 09:49

Thank you Pumizo.

And to the others who have read this guide. Could you please at least say something? Or am i wasting my time here?
On extended hiatus.

User avatar
CobraBlue6
Posts: 1740
Joined: 28 Mar 2015 01:45
Location: Hertfordshire, UK

Re: [GUIDE] [1.20.0.7] Editing game files using a hex editor

#7 Post by CobraBlue6 » 11 Sep 2015 10:42

Interesting stuff Cadde, I've never done any hex editing before so I think I need to do a little more research.

User avatar
R0adrunner
Posts: 690
Joined: 03 Dec 2014 08:43

Re: [GUIDE] [1.20.0.7] Editing game files using a hex editor

#8 Post by R0adrunner » 11 Sep 2015 11:19

I doubt i'll ever become a hex-editor guy myself, but nevertheless; an impressive guide! You're clearly "teacher-material" :geek: Good work!
I've never done that before, so I'm sure I can do it (Långstrump, Pippi)
Hard Times in Scandinavia a.k.a. HTiS (story and pics)
Movin' on - my ATS adventures
My WoT page

User avatar
SimulatorSam
Posts: 6239
Joined: 05 Mar 2014 17:52
Location: United Kingdom
Contact:

Re: [GUIDE] [1.20.0.7] Editing game files using a hex editor

#9 Post by SimulatorSam » 11 Sep 2015 12:44

Interesting read, thanks! :P

User avatar
mwl4
Posts: 104
Joined: 25 Dec 2012 16:06
Location: Poland
Contact:

Re: [GUIDE] [1.20.0.7] Editing game files using a hex editor

#10 Post by mwl4 » 14 Oct 2015 17:57

Code: Select all

/*****************************************************************************
 * ets2_files - Structure definitions for the .pma (0x03 version)
 * file format used in ETS2 and other games by SCSSoft
 *  
 * Copyright (c) mwl4 2014
 *
 *****************************************************************************/

#include "standard-types.hsl"

#pragma byteorder(little_endian) 

typedef quad            int64;
typedef unsigned quad   uint64;
typedef long            int32;
typedef unsigned long   uint32;
typedef short           int16;
typedef unsigned short  uint16;
typedef byte            int8;
typedef unsigned byte   uint8;

typedef uint64          token_t;
typedef int8            bone_id;

struct float3 // sizeof(12)
{
    float x;
    float y;
    float z;
};

struct quat_t // sizeof(16)
{
    float w;
    float x;
    float y;
    float z;
};

struct pma_frame_t // sizeof(56)
{
    struct quat_t   stretch;
    struct quat_t   rot;    
    struct float3   trans;
    struct float3   scale;
};

struct pma_header_t // sizeof(44)
{
    uint32      version;
    token_t     name;
    uint16      frames;
    uint16      flag;
    uint32      bones;
    float       anim_length;
    int32       lengths_offset;
    int32       bones_offset;
    int32       frames_offset;
    int32       delta_trans_offset;
    int32       delta_rot_offset;
};
        
struct _pma_
{
    struct pma_header_t header;
    float               times[header.frames];
    bone_id             bones[header.bones];
    struct pma_frame_t  frames[header.frames * header.bones];
};

// EOF

Code: Select all


/*****************************************************************************
 * ets2_files - Structure definitions for the .pmg (0x13 version)
 * file format used in ETS2 and other games by SCSSoft
 *  
 * Copyright (c) mwl4 2014
 *
 *****************************************************************************/

#include "standard-types.hsl"

#pragma byteorder(little_endian) 

typedef quad            int64;
typedef unsigned quad   uint64;
typedef long            int32;
typedef unsigned long   uint32;
typedef short           int16;
typedef unsigned short  uint16;
typedef byte            int8;
typedef unsigned byte   uint8;

typedef uint64          token_t;
typedef int8            bone_id;

struct float2 // sizeof(8)
{
    float x;
    float y;
};

struct float3 // sizeof(12)
{
    float x;
    float y;
    float z;
};

struct quat_t // sizeof(16)
{
    float w;
    float x;
    float y;
    float z;
};

struct float4x4 // sizeof(64)
{
	float m[4][4];
};

struct pmg_vert_coord_t // sizeof(12)
{
	struct float3 coord;	
};

struct pmg_vert_normal_t // sizeof(12)
{
	struct float3 normal;
};

struct pmg_vert_uv_t // sizeof(8)
{
	struct float2 uv;	
};

struct pmg_vert_color_t // sizeof(4)
{
	uint32 rgba;	
};

struct pmg_face_t // sizeof(6)
{
	uint16 attach0;
	uint16 attach1;
	uint16 attach2;	
};

struct pmg_dummy_t // sizeof()
{
	token_t			name;
	struct float3	position;
	float 			unknown0;
	struct quat_t	orientation;
	int32			name_offset;
};

struct pmg_section_t
{
	token_t		name;
	int32		models;
	int32		model_index;
	int32		dummies;
	int32		dummies_index;	
};

struct pmg_mesh_t
{
	uint32			edges;
	uint32			verts;
	int32			unknown0; // something with uv
	int32			uv_channel;
	int32			bones;
	int32			material;
	struct float3	bb_center;
	float 			bb_diagonal_size;
	struct float3	bb_coord0;
	struct float3	bb_coord1;
	int32			verts_coord_offset;
	int32			verts_normal_offset;
	int32			verts_uv_offset;
	int32			verts_color_offset;
	int32			verts_sec_color_offset;
	int32			verts_tangent_offset;
	int32			faces_offset;
	int32			anim_bind_offset;
	int32			anim_bones_offset;
	int32			anim_weight_offset;
};

struct pmg_bone_t
{
	token_t			name;
	struct float4x4	trans0;
	struct float4x4	trans1;
	struct quat_t	stretch;
	struct quat_t	rot;
	struct float3	trans;
	struct float3	scale;
	float 			sign_determinant_matrix;
	bone_id			parent;
	uint8			pad[3];
};

struct pmg_header_t // sizeof(116)
{
	uint8 			version; // 0x13 version only
	char			type[3];
	int32			meshes;
	int32			sections;
	int32			bones;
	int32			dummies;
	struct float3	bb_center;
	float 			bb_diagonal_size;
	struct float3	bb_coord0;
	struct float3	bb_coord1;
	int32			bones_offset;
	int32			sections_offset;
	int32			dummies_offset;
	int32			meshes_offset;
	int32			dummies_names_offset;
	int32			dummies_names_size;
	int32			anim_binds_offset;
	int32			anim_binds_size;
	int32			geometry_offset;
	int32			geometry_size;
	int32			uv_offset;
	int32			uv_size;
	int32			faces_offset;
	int32			faces_size;
};

struct _pmg_
{
	struct pmg_header_t 	header;
	uint8 					padding[header.bones_offset - 116 /* sizeof(pmg_header_t) */];
	struct pmg_bone_t		bones[header.bones];
	struct pmg_section_t	sections[header.sections];
	struct pmg_dummy_t		dummies[header.dummies];
	struct pmg_mesh_t		meshes[header.meshes];
};

// EOF
To read vertices data:
To improve loading performance, we changed the layout of the file to match the layout of the buffers used by the game. The game can still load the older format albeit slower.
The new 0x506d6713 format stores the vertex attributes as interleaved:
If model is not animatable (bone_count == 0) the attributes for single vertex are stored together in static pool and the stride is sizeof(all vertex attributes)
If the model is animatable (bone_count != 0) the attributes for single vertex are divided into static and dynamic part each having its specific stride
static : sizeof(colors (u32) + colors2 (u32 if present) + texcoords (float2 * coord_count))
dynamic: sizeof(positions (float3) + normals (float3) + tangents (float4 if present))

The offset fields in the header point to corresponding attribute inside the first vertex.
Source: http://eurotrucksimulator2.com/modding_changes.php

Tokens: http://ideone.com/8sfEbp

Post Reply

Return to “Modding Guides”

Who is online

Users browsing this forum: No registered users and 2 guests