TwinsanityEvolved

TwinsanityEvolved is a native PC port of Crash Twinsanity that supports mods and multiple languages.

README

TwinsanityEvolved

Native PC port of Crash Twinsanity: the twinsanity-reversed decompilation (game/) running on the NativeEE engine (engine/). The game is compiled to wasm32 and lowered to C with wasm2c; its linear memory is the engine's 32 MB main memory; the decompilation's own include/platform/*.h is the only seam. On the PS Vita the game's code is compiled natively for the console's ARM processor instead, with the same seam and the 32 MB as a block of the program. No game data of any kind is in this repository — you need your own disc image, which is read at run time.

What's new

Since v0.1.8 (the full list for players is in RELEASE-NOTES.txt):

  • The launcher installs the finished game for Windows, macOS, Linux or the PS Vita from the releases, into one game folder that holds the game, your settings, your memory cards and your mods.
  • The North American disc plays, and every disc is recognised by its content against the known dumps of the original discs.
  • Mods: mods packaged as Mod Crates are supported, as are patched disc images, xdelta / PPF / BPS patches, folders of files and single archive files, added on the launcher's Mods page or with the mod tool.
  • Languages: the text and the speech are two settings, and the game's texts can be read in Arabic.
  • PS Vita: a package in every release, the PAL console's 50 Hz game loop, its movies, shadows and title sequence fixed, and a steadier, faster picture.
  • Fixed: at 50 Hz the game no longer plays in slow motion, on every system.

Play: download the game

  1. From the latest release, download the zip for your system: twinsanityevolved-<version>-windows-x64.zip, -macos-arm64.zip (Apple silicon), -macos-x86_64.zip, -linux-x86_64.zip or -linux-aarch64.zip.
  2. Unzip it anywhere you like, on any drive.
  3. Start twinsanityevolved (twinsanityevolved.exe on Windows). The first time, it asks for your disc image: choose your own Crash Twinsanity image (.iso) -- the PAL disc (SLES-52568) or the North American disc (SLUS-20909 v2.00). That is all: nothing is installed, nothing else is downloaded, and nothing is written outside the game's folder.

The game remembers your disc in disc-image.txt and keeps its memory cards and settings in userdata\ beside it (an earlier install's ~/.twinsanityevolved keeps being used if you have one). Deleting the folder removes everything. The zip holds only the program, the mod tool (twinsanityevolved-modtool, see Mods), the Arabic language pack (mods/arabic, see Arabic), their libraries and the licences: no game data, which is read from your disc while you play (every release zip passes tools/ci/asset_scan.py, which fails on any disc file or game asset).

50 Hz. A PAL console runs the game at 50 frames a second; the port runs it at 60 by default. For the PAL console's own 50 ticks a second, open Options → graphic options → game rate and choose 50 hz (pal): it takes effect the next time the game starts (the setting is game.hz = 50 in userdata/ps2r.ini; --hz 50 does the same for one run). The game moves at the same real-time speed at either rate (its clock counts real time: each 50 Hz frame is 1/50 s of game time). At 50 Hz the frame rate row's 60 shows every frame the game makes, i.e. 50 frames a second.

Loading screens. A load shows the game's loading picture for under a second. On the console a load takes about 12 seconds: its DVD drive reads the level's 13-21 MB at 2.2 MB/s, and the game's loader waits a second before it reads a part of the level, once for the level and once more for the parts linked to it (that wait is meant for play, where a part of the level is read only once you have stayed near it for a second). Here, while the loading picture is up, the disc image is read at the speed of the device you play on, and the loader goes on as soon as its reads are in. What is left is the picture's half-second fade-in and the time your machine needs to read and set up the level: LOAD GAME's two loads, the title's and the hub's, take 41 frames each, 0.7 s on a recent Mac (the reload after a game over 1.1 s: the game over picture fades out first); on the PS Vita a load takes what its memory card and processor need. The level is the one a console-paced load gives, only fewer frames pass. During play the disc keeps the console's pace, so an area that streams in while you walk arrives a piece at a time instead of stalling a frame. For the console's pace in the loads too, set disc.speed = console in userdata/ps2r.ini (--disc-speed console for one run; a run with --deterministic uses it, as the judges do). The memory card messages still show for their two seconds.

The PS Vita's memory card. The PS Vita reads your disc image from its memory card on a thread of its own, and while the game plays the card is slower than the console's DVD drive (1.6-3 MB/s in reads of ~200 KB in the device logs). The game streams a level's parts as you move: the bus chase at the end of Rooftop Rampage reads the next part, 6 MB, while you run at it. So the parts a level's links lead to are read into memory as soon as you enter the part beside them, about a minute before the game asks for them (up to 10 MB of them at a time, of a 16 MB budget); the rest of a file the game starts reading comes off the card in reads of 1 MB; and when the card is late all the same, the game plays on and the part arrives a few frames later, as from a slow DVD, instead of the picture stopping until the card has it. The game's log has the card's figures: [te.io] card: once at start-up (its speed by the size of a read), and in every window [te.io] ... the game waited for the card N times (none in play is the aim), read ahead: ... and [te.stream] ... reads put off for the card. To try a card that slow on a computer or in Vita3K: TE_DISC_ASYNC=1 TE_DISC_SLOW_MS=53 (53 ms for every 128 KB read; on the PS Vita in args.txt); TE_STREAM_WAIT_CARD=1 TE_DISC_AHEAD_MB=0 puts back the old way (the game waiting for the card, nothing read ahead) for a comparison. TE_CHUNK_LOG=1 (on by itself on the PS Vita) writes a line each time a part of the level changes: its files waiting, loading or loaded, and whether the picture drew it ([te.io] chunk loaders @...).

The launcher

The launcher (twinsanityevolved-launcher-windows-x64.zip, twinsanityevolved-launcher-macos-arm64.dmg or twinsanityevolved-launcher-linux-x86_64.AppImage on the release) does all of the above for you. You need two things: your Crash Twinsanity disc image and the launcher. The first time it opens, one sheet asks four questions; nothing is typed, and Back always works.

  1. Where is your game? The launcher looks in Downloads, Desktop, Documents, its own folder and any removable drive, and recognises your disc by what is on it: the region (PAL, NTSC-U, NTSC-J) and whether it is the original disc -- the whole image is compared with the known dumps of the original discs, a few seconds the first time -- or a modified copy ("PAL · modified disc"). Drop the file on the window, or choose it, if it is somewhere else.
  2. Where should the game live? One folder, ~/Games/Crash Twinsanity (%USERPROFILE%\Games\Crash Twinsanity on Windows) unless you choose another. The game, your settings (ps2r.ini), your memory cards (memcards) and your mods (mods) all live in it: move the folder and everything moves with it.
  3. Where will you play? This computer, a PS Vita, or another computer (Windows, macOS, Linux).
  4. Installing. The finished game for that machine comes from the latest release, is checked against the release's checksums and is put in the folder. Then Play.

Step 1: the disc found and recognised Step 2: one folder for everything Step 3: this computer, a PS Vita or another computer Step 4: the download, with its size and speed

After that the launcher has four pages: Games (your discs, each with Play; Update when a new version is out), Mods (the mods that come with the game and the ones in your mods folder, each with a switch and a line saying what it changes; drop a mod crate, a patched disc image, a patch file, a folder of files or one file of the game's archive on the page to add it; drag the mods into order, the lower one wins where two change the same file; a change takes effect the next time the game starts), Settings (the game's own Options in plain language: the same ps2r.ini the in-game menu writes, so a change in one is a change in the other; its Text language and Speech language offer what your disc carries, see Language) and About.

Games Mods Settings

The launcher updates itself. Each time it opens, the launcher looks for a newer launcher in the releases. When there is one, the Games page says A new launcher is ready: vX. with Update the launcher to vX: the new launcher is downloaded, checked against the release's checksums, put in the place of the one you opened, and opened again by itself; your games, settings, saves and mods stay as they are (About has Check for a newer launcher). Where it cannot replace itself -- a folder your account cannot change, such as Applications for a standard account, or the launcher opened straight from its disk image -- it says why, and Download it opens the release's page. The game itself is updated as before, with Update on its card. On macOS the launcher is not signed by Apple: after an update macOS may ask again before the launcher reads a disc image in Downloads, Desktop or Documents.

A newer launcher, offered on the Games page

PS Vita. Choose PS Vita in step 3 (or Get the game for PS Vita or another computer… on the Games page) and the launcher makes a folder ready to copy, vita/ in your game folder: twinsanityevolved.vpk, README-vita.txt with the steps, and ux0/data/twinsanity/ as the console's memory card has it -- your game folder's settings (ps2r.ini, without the computer's own folders, picture size, window and game rate: the PS Vita has its own), a copy of the language packs and of the mods you switched on, and disc-image.txt naming your disc's file name. Then:

  1. Copy twinsanityevolved.vpk to the PS Vita and install it with VitaShell.
  2. Copy ux0/data/twinsanity to the PS Vita's memory card, so that it is ux0:data/twinsanity there.
  3. Copy your own disc image into ux0:data/twinsanity/ under the name disc-image.txt gives (the launcher never copies the disc image; without disc-image.txt the game starts ux0:data/twinsanity/game.iso).
  4. The PS Vita needs its shader library ur0:data/libshacccg.suprx -- the usual homebrew step. The launcher never downloads or carries this file.

After you import a mod, switch one on or off or change a setting, Make the PS Vita folder again -- at the foot of the Mods page, under PS Vita, and on the Settings page -- brings the folder up to date (only what changed is copied); copy ux0/data/twinsanity again. Mods are added and switched on the computer, never on the PS Vita. The launcher's About page has the whole PS Vita flow in six sentences. On the PS Vita, ux0:data/twinsanity/disc-image.txt switches discs: two can sit side by side (game.iso and, say, ntsc.iso) and the file's one line names the one the game starts with (a file name beside game.iso, or a full ux0: / uma0: path). The North American disc plays there as on a computer -- at 60 Hz (the PS Vita's 50 Hz default is the PAL disc's), with its own memory card (ux0:data/twinsanity/memcards-SLUS-20909) and English only in the two language rows; the mods in ux0:data/twinsanity/mods and the Arabic language pack work as they do on a computer. Importing a mod is the launcher's job, on a computer. The PS Vita's resolution row offers 1x or 2x: 2x, the console's maximum, runs at half the frame rate or less, and a ps2r.ini that names 3x or 4x is read as 2x. The package runs the game's code natively on the console's ARM processor.

The Mods page's PS Vita panel

The launcher keeps only its own notes (the discs it knows, its window, its logs) in its app-data folder (%LOCALAPPDATA%\TwinsanityEvolved on Windows, ~/Library/Application Support/TwinsanityEvolved on macOS, ~/.local/share/TwinsanityEvolved on Linux); deleting that folder removes them. The game folder is yours. Every screen of the launcher is in docs/launcher/screenshots/.

Mods

Mods change what the game reads from your disc, never the disc image itself. Add one on the launcher's Mods page (drop it on the page, or Add a mod…) or with twinsanityevolved-modtool, which comes with the game, and switch it on there: the launcher's Mods page is where the mods of your mods folder are switched, and the game starts with them in place. A change takes effect the next time the game starts. What a mod is, is decided by what is in it, never by its name; mods are taken as the community ships them:

  • Mod Crates -- the community's usual shape, made for Crate Mod Loader: a .zip or a folder with ModCrateInfo.txt, layer0/ (files of the disc) and layer1/ (entries of the game's archive). A large crate (over a gigabyte) takes about half a minute to add. The author's notes in ModCrateInfo.txt are shown under the mod on the launcher's Mods page.
  • A patched disc image (.iso): only the files that differ from your disc are kept.
  • A patch made against the original disc -- xdelta, PPF or BPS: applied to a temporary copy of your disc (its free space is checked first; the copy is removed afterwards), then kept as the files that differ.
  • A folder of files laid out like the disc, loose files such as a whole CRASH.BD + CRASH.BH, or a folder that is already a mod (mod.json).
  • One file of the game's archive -- what the Twinsanity Editor writes: a level chunk (.rm2, .sm2), a picture (.psm), a text bank (.txt), passed around bare like a Default.rm2. It is placed by its name: the one entry of the archive with that name is replaced (Default.rm2 becomes the mod default). A name several entries share (English.txt) is refused with them listed: put the file in folders named like the one it replaces.

Each becomes one folder of your mods folder (<game folder>/mods/<id>/, described by its mod.json), holding only what the mod changes. Where two mods change the same file, the lower one on the launcher's Mods page wins.

The same mod again replaces it. A mod's id is its name (My Level Pack -> my_level_pack); adding a mod whose id you already have -- a new version of it, or the same file again -- replaces the one you have once the new one is complete. The launcher asks first ("Replace ? The copy you have is removed first."; Cancel adds nothing), the mod tool replaces it; the mod keeps its switch and its place in the order. A folder of that name that is not a mod is never touched: the addition stops and says so.

twinsanityevolved-modtool import "a mod crate.zip" --iso "/path/to/your disc.iso" --mods "<game folder>/mods"
twinsanityevolved-modtool inspect "a patch.xdelta" --iso "/path/to/your disc.iso"       # what it is, without adding it
twinsanityevolved-modtool verify "<game folder>/mods/<id>" --iso "/path/to/your disc.iso"   # every file, byte for byte

A mod is made for your disc. Its folder records the files it was made over, and the game checks them when it starts: a mod added with one disc and played with another (whose files differ) is left out, with a line in the game's log. The European and the North American discs are different discs: a mod made for the North American disc carries the NTSC-U badge on the launcher's Mods page, and on a European game folder its row says "made for the North American disc"; when it was made over that disc's files (an imported crate's archive is), the game leaves it out at its start and the row says so in amber: "Left out on this disc: made for the North American disc." One file of the archive made for the other disc is refused when it is added ("... it was made for the European disc").

A crate made for the North American disc and added over the European one plays with the European disc's text files: texts its level scripts add to the North American file (Language\AgentLab\American.txt, which the European program never reads) are not there, so the signs that show them stay empty, and the game's log names each one ([te.mods] tolerated: a script shows text 110, the scripts' text file has 100 lines: shown empty). Added over the North American disc, the crate has all its texts.

Not supported -- refused with a sentence saying why, or listed under the mod:

  • Code changes in a modified executable. A mod's SLES_525.68 is used for its data (tables, texts, numbers: those apply), but this port runs its own code, not the executable's: changed code is listed under the mod on the launcher's Mods page ("Code changes not supported: ...") and does not apply (new code a crate adds is such a change).
  • ModCrateSettings.txt -- the property edits Crate Mod Loader makes to the game's code -- and crates with scripts. The same settings asked for in a crate's notes ("apply Classic Slide Jump for Crash and Classic Explosion Damage") cannot be applied either: the mod is added, and its row says so in a warning.
  • xdelta patches with secondary compression (-S djw, fgk or lzma; xdelta3 3.1 with LZMA support uses LZMA by default): apply such a patch yourself (xdelta3, Delta Patcher) and add the patched image.
  • RAR and 7-Zip archives, CHD / CSO / ECM images, .cue sheets, UPS and IPS patches, encrypted zips; emulator texture packs and cheat codes (they are not disc content).

The built-in mods come with the game, off by default:

  • level select — one row that opens the list of the game's levels, bosses and areas at once; choosing one starts it through the game's own saved-level path (from the main menu as a new game would, from the pause menu with the current progress). The level starts with the character its own start gives, alone: nothing of the level left comes along (Cortex beside Crash, the Rollerbrawl of Cavern Catastrophe).
  • speedrun mode — one real-time timer (mm:ss.cc) in the game's font, centred at the top of the safe area, started at NEW GAME or the first level entered. The in-game time and a split on every level entry / exit are written into a --record-pad recording (# split … lines: a recorded run carries its timer and its splits). Practice save states on a ring of 8: F9 saves, F10 loads the newest in place, F11 the one before (~/.twinsanityevolved/practice/); F12 or Mods > speedrun > reset to run start resets.
  • infinite lives — while it is on, a death costs no life: the lives panel still comes up at each death, showing the number it showed before, and the restart at the checkpoint never ends in a game over. The game's font has digits only, so there is no infinity sign: the count simply stops going down. Lives you collect still count (a hundred wumpa fruit, an extra life). Switched off, the count goes on from where it is.

In the game, OPTIONS > MODS lists the built-in mods, which apply at once: level select, speedrun mode : off|on and infinite lives : off|on. The mods of your mods folder