• 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.

[Tut] How to (partially) read SCB Files in C#

Joined
Aug 14, 2009
Messages
2,304
Reaction score
1,189
In this tutorial, I will show everybody how to partially read SCB Files in C#. But you can convert my code into any language you want.

First what's the SCB file? It's basically a SCO file but in binary format. So instead of saving everything as text, it saves it as binary values.

So first the structure of the SCB format.

Version - unsigned int
Flags - unsigned long
Name Length - int
Name - /\ char sizeof name length
Pivot - 12 bytes (3 float numbers)

So first create a FileStream using System.IO:
Code:
FileStream fs = File.Open("C:\\Models\\Target_01.scb", FileMode.Open);

Now you can create a BinaryReader we use them to read the actual binary file.
Code:
BinaryReader br = new BinaryReader(fs);

So now for the actual reading:

As I said earlier, the File Version is a unsigned int. In C# it's uint. Because it's an uint not an ushort, we have to use ReadUInt32. Not 16.
Code:
uint fileVersion = br.ReadUInt32();

The File Flags are an unsigned long. In C# it's ulong. Because in 32-Bit C++ a unsigned long is 4 bytes we have to use ReadUInt32 again. Not 64.
Code:
ulong fileFlags = br.ReadUInt32();

Now for reading the name length & name. We can't use the method ReadString from BinaryReader because of null terminators. So we have to read the length and then all chars.
Code:
// Read the name length
int nameLength = br.ReadInt32();
// Read namelength chars
string name = new string(br.ReadChars(nameLength));

Finally we come to the tricky part. Because Eternity / SCB files use r3dPoint3D to save we have to figure out how big r3dPoint3D is. In this case it's 12 bytes. Because X, Y, Z (3) multiplied by 4 (4 bytes for each float / int). We can easily read 12 bytes then split them up, or we could read 4 bytes at a time.
I'll read 12 bytes and then cut the bytes to 4 individual pieces.

We read 12 bytes:
Code:
byte[] buffer = br.ReadBytes(12);

now we create a new variable that is a byte array with 4 bytes:
Code:
var x = new byte[4];

and copy the first 4 bytes to our buffer
Code:
Array.Copy(buffer, 0, x, 0, 4);

Now the same thing for our 4 next bytes:
Code:
var y = new byte[4];
Array.Copy(buffer, 4, y, 0, 4);

And again for the last 4 bytes:
Code:
var z = new byte[4];
Array.Copy(buffer, 8, z, 0, 4);

Now we have our x, y, z pivot. We can now convert the byte pivot coordinates using the BitConverter ToSingle method:
Code:
System.BitConverter.ToSingle(x, 0)

If you like this Tutorial click the like button ;). If I should do the rest of the file just lemme know!
 
Last edited:
Elite Diviner
Joined
Feb 18, 2014
Messages
440
Reaction score
232
Not understand what it means.

SCB files are in binary format, and they are unable to be read. This tutorial makes it partially readable like an .SCO.

More less: .SCO's are the model file that I:SS reads and uses.
 
Rival Gamers Owner
Loyal Member
Joined
Jul 7, 2007
Messages
962
Reaction score
161
thanks, this is at least 90% done, i got up to reading material names

c#:
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace Scb_Converter
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        struct ThreeD
        {
            public float x, y, z;
        };

        ThreeD ConvertTo3D(byte[] data)
        {
            ThreeD Data;
            var temp = new byte[4];
            Array.Copy(data, 0, temp, 0, 4);
            Data.x = BitConverter.ToSingle(temp, 0);
            Array.Copy(data, 4, temp, 0, 4);
            Data.y = BitConverter.ToSingle(temp, 0);
            Array.Copy(data, 8, temp, 0, 4);
            Data.z = BitConverter.ToSingle(temp, 0);
            return Data;
        }

        struct TwoD
        {
            public float x, y;
        };

        TwoD ConvertTo2D(byte[] data)
        {
            TwoD Data;
            var temp = new byte[4];
            Array.Copy(data, 0, temp, 0, 4);
            Data.x = BitConverter.ToSingle(temp, 0);
            Array.Copy(data, 4, temp, 0, 4);
            Data.y = BitConverter.ToSingle(temp, 0);
            return Data;
        }

        struct Material
        {
            public int Start, End, Unknown;
        };

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog d = new OpenFileDialog();
            d.DefaultExt = ".scb";
            if (d.ShowDialog() == DialogResult.OK)
            {
                using (FileStream fs = File.Open(d.FileName, FileMode.Open))
                using (BinaryReader br = new BinaryReader(fs))
                {
                    if (br.ReadUInt32() != 4208721976)
                    {
                        MessageBox.Show("Scb file not supported!", "Version Mismatch", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }
                    ulong fileFlags = br.ReadUInt32();
                    int nameLength = br.ReadInt32();
                    string name = new string(br.ReadChars(nameLength));
                    ThreeD CenterPoint = ConvertTo3D(br.ReadBytes(12));
                    int Verts = br.ReadInt32();
                    ThreeD[] VertexPositions = new ThreeD[Verts];
                    TwoD[] VertexUVs = new TwoD[Verts];
                    ThreeD[] VertexNormals = new ThreeD[Verts];
                    ThreeD[] VertexTangents = new ThreeD[Verts];
                    char[] VertexTangentWs = new char[Verts];
                    for (int i = 0; i < Verts; i++) VertexPositions[i] = ConvertTo3D(br.ReadBytes(12));
                    for (int i = 0; i < Verts; i++) VertexUVs[i] = ConvertTo2D(br.ReadBytes(8));
                    for (int i = 0; i < Verts; i++) VertexNormals[i] = ConvertTo3D(br.ReadBytes(12));
                    for (int i = 0; i < Verts; i++) VertexTangents[i] = ConvertTo3D(br.ReadBytes(12));
                    for (int i = 0; i < Verts; i++) VertexTangentWs[i] = br.ReadChar();

                    int NumIndices = br.ReadInt32();
                    uint[] Indices = new uint[NumIndices];
                   // for (int i = 0; i < NumIndices; i++) Indices[i] = br.ReadUInt32();
                    //int MaterialCount = br.ReadInt32();
                    int faces = NumIndices / 3;
                    Material[] mat = new Material[faces];
                    for (int i = 0; i < faces; i++)
                    {
                        mat[i].Start = br.ReadInt32();
                        mat[i].End = br.ReadInt32();
                        mat[i].Unknown = br.ReadInt32(); // not sure why
                    }
                    br.ReadBytes(12); // 12 bytes of useless data ????
                    int nameLength1 = br.ReadInt32();
                    string name1 = new string(br.ReadChars(nameLength1)); //weirdly have to load the name after the array
                    MessageBox.Show(name1);


                   // ThreeD[] Test = new ThreeD[faces+1];
                    //for (int i = 0; i <= faces; i++) Test[i] = ConvertTo3D(br.ReadBytes(12));
                  //  for (int i = 0; i < Verts; i++) MessageBox.Show(VertexPositions[i].x + " " + VertexPositions[i].y + " " + VertexPositions[i].z + " " + VertexNormals[i].x + " " + VertexNormals[i].y + " " + VertexNormals[i].z + " " + VertexTangents[i].x + " " + VertexTangents[i].y + " " + VertexTangents[i].z + " " + VertexTangentWs[i]);
                   // MessageBox.Show("Material Count: " + MaterialCount);
                }
            }
        }
    }
}

c++:
Code:
#include "stdafx.h"
#include <Windows.h>
#include <stdint.h>

struct ThreeD
{
	float x, y, z;
};

struct TwoD
{
	float x, y;
};

struct Material
{
	int StartIndex, EndIndex, Mat;
	char* MatName;
};

struct SCB
{	
	uint32_t version, * Indices;
	DWORD flags;
	int NameLength, Verts, NumVertices, NumIndices, NumMatChunks;
	char Name[128];
	ThreeD	CenterPoint, *VertexPositions, *VertexNormals, *VertexTangents;
	TwoD*   VertexUVs;
	char*	VertexTangentWs;
};

SCB Load(char* Filename)
{
	SCB s;
	s.version = -1337;
	FILE * f = fopen (Filename , "rb");
	if (f==NULL) return s;
	fread(&s.version, sizeof(uint32_t), 1, f);
	if(s.version != 0xFADC0038)
	{
		printf("Incorrect Version: %d, Wanted: %d\n", s.version, 0xFADC0038);
		return s;
	}	
	fread(&s.flags, sizeof s.flags, 1, f);
	fread(&s.NameLength, sizeof(s.NameLength), 1, f);
	memset(s.Name, 0, sizeof(s.Name));
	fread(s.Name, s.NameLength, 1, f);
	fread(&s.CenterPoint, sizeof(ThreeD), 1, f);	
	fread(&s.NumVertices, sizeof(s.NumVertices), 1, f);
	s.VertexPositions = new ThreeD[s.NumVertices];
	s.VertexUVs = new TwoD[s.NumVertices];
	s.VertexNormals = new ThreeD[s.NumVertices];
	s.VertexTangents = new ThreeD[s.NumVertices];
	s.VertexTangentWs = new char[s.NumVertices];
	fread(s.VertexPositions, sizeof(ThreeD)*s.NumVertices, 1, f);
	fread(s.VertexUVs, sizeof(TwoD)*s.NumVertices, 1, f);
	fread(s.VertexNormals, sizeof(ThreeD)*s.NumVertices, 1, f);
	fread(s.VertexTangents, sizeof(ThreeD)*s.NumVertices, 1, f);
	fread(s.VertexTangentWs, sizeof (s.VertexTangentWs[0]) * s.NumVertices, 1, f );
	fread(&s.NumIndices, sizeof(s.NumIndices), 1, f);
	//s.Indices = new uint32_t[s.NumIndices];
	//fread(s.Indices, sizeof(uint32_t)*s.NumIndices, 1, f);
	int Faces = s.NumIndices / 3;
	printf("Faces: %d\n", Faces);
		int a, b, c;
	for(int i = 0; i< Faces; i++)
	{
		fread(&a, sizeof(int), 1, f);
		fread(&b, sizeof(int), 1, f);
		fread(&c, sizeof(int), 1, f);
	}
	
	fread(&a, sizeof(int), 1, f); // Read 4 bytes of crap????
	fread(&b, sizeof(int), 1, f);// Read 4 bytes of crap????
	fread(&c, sizeof(int), 1, f);// Read 4 bytes of crap????
	int len = 0;
	char test[128];
	fread(&len, sizeof(len), 1, f);
	memset(test, 0, sizeof(test));
	fread(test, len, 1, f);
	printf("Material Name: %s\n", test);
	return s;
}

int _tmain(int argc, _TCHAR* argv[])
{
	SCB* s = &Load("F:/RivalZ/bin/Data/ObjectsDepot/Weapons/asr_ak74_fps.scb");
	if(s && s->version != -1337)
	{
		printf("Flags: %d\n", s->flags);
		printf("Name: %s\n", s->Name);
		printf("CenterPoint: %f - %f - %f\n", s->CenterPoint.x, s->CenterPoint.y, s->CenterPoint.z);
		printf("Vertices Count: %d\n", s->NumVertices);
		printf("Indices Count: %d\n", s->NumIndices);
		printf("Faces Count: %d\n", s->NumIndices/3);
		//printf("Material Name: %s\n", s->MatChunks[0].MatName);
	} else printf("Failed to load .Scb File\n");
	system("pause");
	return 0;
}

just a tiny bit left for people to do



someone else can release the rest i believe i have provided enough
 
Last edited:
Master Summoner
Joined
Mar 30, 2013
Messages
543
Reaction score
72
Thanks Giga! It would be cool if you figured out how to extract .bin files in C#, or any .Net language. Looks like they use zlib compression.
 
Junior Spellweaver
Joined
Apr 14, 2013
Messages
185
Reaction score
66
Thanks Giga! It would be cool if you figured out how to extract .bin files in C#, or any .Net language. Looks like they use zlib compression.
zlib is used indeed. You'll need to use P/Invoke too (GetSystemInfo, CreateFileA, CreateFileMappingA, MapViewOfFile, UnmapViewOfFile, CloseHandle, GetAdaptersInfo, SHCreateDirectoryExA, PathCompactPathExA)
There's a C++ source out there that extracts these .bin files, if you want to convert it to C# :)
Or you can use my app: https://forum.ragezone.com/f790/release-ss-archive-extractor-1001853/
 
Back
Top