SCCT Reforge
A tool that rebuilds the models, textures and HUD of Splinter Cell: Chaos Theory. Free build and early access, restorable byte for byte.

What it is
A tool that rebuilds the models and textures of Splinter Cell: Chaos Theory. I still play it, and one evening I noticed the suppressor on Samâs rifle had eight sides. Thatâs where it got out of hand.
Nothing gets swapped, everything gets rebuilt. And if you donât like it, one click puts your game back exactly the way it was.
What it does
- Rebuilds the weapons, some brought over from Double Agent
- Gives Sam a proper modern look: head, body, goggles
- Adds six extra outfits and one high-poly suit
- Redraws the HUD so itâs sharp on todayâs screens
- Upscales mission images and loading screens
- Restores everything, byte for byte
Download
One download for everyone. The free build stays free. With Plus you get the keys that unlock the full version, right next to the download.
SCCT Reforge
Rebuilds the models, textures and HUD of Splinter Cell: Chaos Theory â and restores everything with one click.
1Download
Version 1.10 Early AccessFree
Windows 10/11 (32- and 64-bit) ¡ .exe ¡ 77.4 MB ¡ Oct 10, 2026
Checksum (SHA-256)
1c19a2b8923522bd6afb5713bb9de9d9530264b1c8466ea3edf297906c6590f9
2Unlock the full versionPlus
The download is the same for everyone. You unlock the full version with all features in the tool using a key â with Plus, the keys are right here. Plus costs âŹ7.95 a month or âŹ79.95 a year (works out to âŹ6.66 a month).
What you need
- Your own copy of Chaos Theory (PC), on Windows
- For the Double Agent parts: your own copy of Double Agent
Unzip, run, pick what you want, install. You can restore the original whenever you like.
Found something weird?
Thumbs sticking out, a neck that vanishes when Sam crouches: almost every real bug was found by someone playing. If you see one, tell me. Iâm always happy about testers.
Dev log
For the curious: how the tool came together, what went wrong and what I learned from it.
Read the dev log
01 ¡ Where this started
Chaos Theory came out in 2005 and I still play it. One evening I looked at the suppressor on Samâs rifle and noticed it was an octagon. Not a cylinder with a few segments, an actual eight-sided prism, sitting in the middle of the screen for the entire game.
Thatâs not a complaint about the artist. In 2005 you had a triangle budget and you spent it where people looked. Itâs just that I have a computer from 2025 and the game doesnât. So: how hard can it be to add sixteen more sides to a cylinder?
The answer is that the game stops starting, and it takes about a week to find out why.
Everything single-player in Chaos Theory sits in one file: 610,578,839 bytes, 20,252 entries, 16-byte aligned, gaps zero-filled. Inside it are Unreal Engine 2 packages, 13,385 static meshes and 12,035 images across 72 texture packages. None of that is documented. Thereâs no SDK, no import plugin, and the generic UE2 notes mostly donât apply because this is a fork. So the first month wasnât modding at all. It was writing tools that tell you what a byte means.
One rule from the start, and Iâve never regretted it: anything I claim about the format has to hold across the whole container, not on one file. A theory that works for one mesh is a coincidence. A theory that works for 13,381 out of 13,385 is a format.
02 ¡ The suppressor that refused to shrink
Hereâs the experiment that cost a week and taught me the most.
There is a mesh called FN2000_SAM_Silencer. It has 27 vertices and 22 triangles and, measured, the game never draws it. The visible suppressor is baked into the rifle frame. Perfect guinea pig: shrink it from 27 vertices to 3 and from 22 triangles to 1, change nothing else.
The game doesnât start any more. The same container with geometry appended to that mesh instead of removed runs perfectly.
The reason sits in a block after the vertex data that I originally labelled ârestâ because I couldnât read it. Itâs a collision tree, and its 16-bit values are two different things wearing the same coat: if the top bit is set itâs a triangle number, otherwise itâs a link inside the tree. Measured across the whole game, every one of those triangle numbers is below the meshâs own triangle count. So when the counters grow, every reference stays valid. When they shrink, they point past the end, and the game dies before it draws a single frame.
Which gives the one law everything else is built on: existing vertices are never removed and never reordered, only appended. And that quietly solves importing arbitrary models too. If the new model has fewer triangles than the slot itâs going into, you fill the rest with degenerate triangles, the same vertex three times. Zero area, never drawn, every triangle number still valid. A test cube of 8 vertices and 12 triangles dropped into the reflex sightâs slot produces an object of exactly the same 7,168 bytes as before, with all 300 other objects in the package byte-identical.
03 ¡ The rebuild that wouldnât boot
For a long time a second rule stood next to the first: nothing may move. Because when I rebuilt that 610 MB container from scratch, the game wouldnât boot, even though the rebuild was byte-exact with no changes applied at all.
That was true, and it was true for the wrong reason. I spent real time on the checksum in the footer before finding out the game never checks it. The actual cause was in the meshes: every one of them ends with a lazy array whose jump marker is an absolute position in the package file. Move the object, and the marker points at nothing.
| state | markers correct | wrong | game |
|---|---|---|---|
| original | 270 | 0 | runs |
| patched in place | 270 | 0 | runs |
| grown, markers untouched | 112 | 158 | wonât start |
| grown, markers recomputed | 270 | 0 | runs |
That last row is confirmed in the actual game. So the rule became: objects may move and grow, the jump markers move with them. And the reason patching in place had always worked was luck disguised as discipline. The object keeps its exact byte count, so its trailing block starts at the same address.
Before I found this, I built something Iâm still fond of even though itâs now mostly obsolete. A ring swap: an object moves into the slot of a larger one, the larger one into a smaller one, and the chain closes. Offsets and sizes in the export table are only exchanged, never changed, so the table is necessarily the same length, no header offset moves, and the file stays the same size. It buys you 28,915 bytes where you had 7,168, with no rebuild at all. Itâs still in the codebase. Itâs just that once rebuilding works, you can simply let the package grow.
04 ¡ Sam, and the eighty-nine percent
Modernising the player character was supposed to be a side quest. It became the largest single piece of work in the project.
Samâs head is 4,076 triangles. Subdividing it naively makes it rounder and worse, you get a smooth potato. What actually works is laying over every edge the curve that meets the stored normal at both ends. The roundness was already in the model all along, the lighting knew about it and the silhouette didnât. It also needs no crease detection whatsoever, because at a real hard edge the normals are split and sit on the facets, the correction term goes to zero, and the midpoint stays on the straight line. Sharp edges stay sharp without anyone looking for them.
Result: 4,076 to 35,330 triangles, median crease angle from 21.8 down to 5.8 degrees. And he still looked flat.
That was the surprise, and the measurement that explains it is my favourite number in the whole project. A scheme like that leaves the original vertices where they are, so nothing can appear that wasnât already there. But the normal map knows the slope at every point, and a slope is the derivative of a height. Reconstruct that height and you can measure exactly how much of the surface form was ever geometry:
| displacement span | slope in the mesh | share of the map |
|---|---|---|
| 0.45 (where I started) | 2.7° | 10.8 % |
| 1.75 (shipping) | 9.5° | 38.4 % |
| 3.00 | 17.4° | 72 % |
Eighty-nine percent of the surface form was painted shading. Brows, lips, suit folds, vest seams, none of it existed as geometry. Thatâs why Sam looked flat with forty thousand triangles. The triangles went into rounding, not into shaping.
Then it tore his face open. At a fixed displacement the mouth opened down to the teeth and the eyelid creases carved in. Measured rather than guessed: the upper lip travelled 0.57 units across a gap of a tenth, and 484 triangles flipped against their own stored normal, clustered at face, hands and calves. Thatâs not random, itâs the map of the original mesh. The artist built fine where the forms are small, 1.6 units per edge in the face against 7.2 at the shoulder. A displacement that reads as muscle definition on a shoulder reads as a torn-open mouth on a lip. So it scales per vertex with the local edge length now, and the face runs at about a quarter speed.
The lesson I keep coming back to: a closed gap can still be visible. In the end no mouth pair opened more than 0.09 units, and there were still wedges at the lip line, because the lip edge itself had rolled and exposed a slit that was always open. Distance metrics check distances. What you actually see is only checked by looking at it.
05 ¡ Three months of an eyeball
Chaos Theoryâs successor, Double Agent, runs on a fork of the same engine, and its models read with my Chaos Theory reader almost unchanged. So its weapons, its goggles and its outfits can be transplanted. Transplanted is the honest word, nothing is swapped: every foreign mesh gets re-bound to Chaos Theoryâs skeleton, its triangle strip re-woven, its texture sheets re-encoded and re-packed.
The re-binding is where all the difficulty lives, and the best story in this project is Samâs eye.
The obvious approach is that every transplanted point inherits the skin weights of the nearest Chaos Theory point. That works beautifully for skin and terribly for anything rigid. An eyeball rotates, it does not deform. Inherited from the surrounding surface it got 86 different blended bindings and not a single eye bone, and in game it visibly warped when Sam looked around or squinted.
I fixed it three times. Each fix shipped, and each one broke something new. Making it rigid on the eye bone tore it at the eyelid. Scaling the weights up instead gave a wobbling mask and stiff eyes. Moving the ball onto the centre of the visible eye made it sit too low. All three were rolled back.
And the reason every single attempt missed is that I was fitting spheres to visible geometry instead of reading the skeleton, which turned out to be sitting inside the mesh the whole time. Named bone records: a compact index for the name, then 56 fixed bytes of flags, rotation, position, children and parent. Variable record length, which is why my earlier scans only ever found part of the table.
| distance from the actual bone | |
|---|---|
| Chaos Theoryâs visible eye | 0.79 behind it |
| Double Agentâs eyeball | 0.80 in front of it |
Chaos Theoryâs own eye doesnât sit on its own pivot. The entire sphere-fitting premise was wrong. And the mechanism is different from what Iâd assumed: the eye section has 30 points, 14 of them bound to the head and only 8 to each eye bone. Itâs a small iris cap sliding across a stationary background under partially dragged eyelids. Nothing ever rotates as a whole ball, which is exactly why the bone offset never mattered in the original.
If you need a pivot, read the skeleton. Fitting a sphere to what you can see measures the shape, not the mechanism.
06 ¡ Learning to read bug reports
The most useful thing that came out of testing with an actual player isnât a fix, itâs a vocabulary. Three reports arrived over a few weeks: âit tearsâ, âthe chin comes outâ, âit folds upâ. I treated them as the same complaint three times. They are three different quantities, and each of my probes was blind to the other two.
- âtearsâ is edge stretch, a maximum.
- âcomes outâ is migration relative to the carrying bone.
- âfoldsâ is compression, the exact opposite of stretch, and I had simply never counted it.
A rig fix that halves the stretch can double the compression, and my entire test suite would have reported success. There is now a probe that drives 40 real poses across 22 body regions and measures all three, plus the opening of gaps.
Related, and it took me embarrassingly long to see: short edges tear quietly. Sorting by absolute stretch, a tenfold rip on a one-unit collar edge hides underneath the perfectly healthy stretch of a long knee edge. The original models show ratios of 13 to 19 on short eyelid edges and theyâre fine. Only the location gives the bug away.
07 ¡ The HUD that simply wasnât there
The HUD lives outside the package system entirely, as plain image files in a separate branch of the container. 78 sheets, most of them 16 by 16 or 32 by 32 pixels, stretched onto the screen and filtered, which at 1440p is mud.
They arenât photographs though, theyâre analytic geometry. Discs, rings, arcs with gaps, wireframe renders of the actual weapon models, all of it rasterised onto a tiny grid in 2005. So you donât upscale them. You build a parametric model, render it large, scale it back down to the original size, and push the parameters until it matches. The return path is the proof: a reconstruction you canât scale back down and compare is a claim, not a result. 66 of the 78 now ship reconstructed, and the compass ruler returns at 0.00 percent mean deviation.
And then the new sheets werenât in the game. Four rounds of ânot visibleâ. Rounds one to three each found a real bug that wasnât the cause. The actual answer is one stage behind the image loader: it reads the dimensions out of the file header correctly, and then the texture objectâs setup call immediately overwrites them with the dimensions it was created with, which come from the menu layout table. A 512 pixel sheet lands in a 32 pixel surface. No crash, no log, just nothing.
A sheet may grow, but only together with its layout. Two correct pieces of evidence donât make a proof when thereâs a stage missing in between. Since then the installer verifies itself: after writing, the container is reopened, every sheet read back out and compared byte for byte, and only then does the tool say so.
08 ¡ A foreign suit, and filler that was never content
The most recent stretch of work was a user-built suit, hundreds of thousands of triangles with sheets baked in a modern renderer, going onto a 2005 skeleton where the whole model has to fit into a triangle strip of 65,535 indices. Four findings, all of which generalise.
The filler on a baked sheet is not content. The suit showed fanning streaks across chest, flank and thigh. Four counter-checks cleared my import path completely, and the cause was in the bake: it covered 27 percent of the sheet, the rest is edge extension. The modelâs own coordinates cover 49 percent. So half the body area sits on filler. You canât recover it, but you can regenerate it as a smooth continuation instead of a starburst. What distinguishes real bake from filler is measured, not guessed: brightness and saturation donât separate them at all, fine-grained variance and directionality do, and either one alone fails.
What a model brings that no texture knows about is geometry. The raised web ridges are 449,560 triangles carrying no texture coordinates at all. I originally dropped them, reasoning the pattern was in the sheets anyway. That reasoning was a claim about the data, and the data disproved it: a full-resolution crop of the torso shows the fine honeycomb and no ridge anywhere. Theyâre baked into the normal map from the geometry now, which is the right place for them, since they only stand 0.23 units proud.
A bone has to sit inside the flesh it moves. The thumb stuck out and was rigid, and thatâs one cause, not two: the thumbâs flesh ran 19.9 degrees off its bone chain, up to eight units alongside it. The handâs weight field falls off with the fourth power of distance, so at that range thumb, palm and finger bones are all equally far away and equally weak. The thumb was hanging on an average of half the skeleton. Rotated back onto its chain itâs 3.0 degrees, and the fingertipâs travel under a bend went from 2.64 to 5.87 units, against the original Samâs 7.34.
A seam runs through an island, not only between islands. The web lines broke along the underside of the forearm. A checkerboard render, rather than the suit texture, showed it wasnât shading at all, it was the coordinates: the distortion measure hit 20.4 on the underside against 1.4 on top, and the worst triangles had two corners at one end of the sheet and one 470 texels away. Thatâs the wrap seam of the arm, and a cut tube is still topologically one island, so filtering candidates by island number never saw it. One extra condition later, the underside is at 1.9.
09 ¡ Things I got wrong, in public
This section exists because the mistakes that cost the most were never the hard problems.
I put a convention to a vote. Whether the vertical texture axis counts from the top or the bottom: I compared coverage against baked content, got 60 percent against 35 in favour of flipping it, and flipped it. Half a build later, 9.4 percent of the model was landing on empty atlas and there were black bands across belly, mask, forearm and boots. Correct orientation: 0.05 percent. The answer had been sitting in the code that reads the sheets the entire time. Whoever puts a convention to a vote gets a majority, not an answer.
I measured with a different tool than the one that builds. My texture budget analysis used a simple shelf packer and reported that repacking the atlas would make things worse. The build uses a proper rectangle packer. With the real one itâs a 1.78x gain, and I nearly closed the question on the strength of a tool measuring itself.
I shipped a rig change whose effect my own probes couldnât see. It produced âhalf his neck disappears at the frontâ in game, and offline it was, and remains, undetectable. Sections unchanged, seams unchanged, renders fine, every number clean. It was removed entirely. A change whose effect your probes cannot see isnât verified, itâs unverifiable, and it doesnât ship.
And a guard that never fires looks exactly like a guard. Several times over: a comparison seeded at a value every real candidate fell below, so nothing ever won; a clamped check that dutifully reported zero error on every mesh in the game. Every threshold in the build has since been deliberately tripped once, just to watch it actually break the build.
Unofficial fan project, not affiliated with Ubisoft. Splinter Cell, Chaos Theory and Double Agent are trademarks of their respective owners.






Leave a comment
You must be logged in to post a comment.