Collection of various tools for PC ports of Metal Gear / Metal Gear 2: Solid Snake!
Every tool is lossless: a file exported and imported back unchanged gives the very same bytes. Texts go to translation
tools as XLIFF 2.0, XLIFF 1.2 or Gettext PO, pictures and glyphs become PNG images, structures JSON, and data no tool
decodes stays in .bin files.
| Release | Games |
|---|---|
| Metal Gear (GOG) | Metal Gear |
| METAL GEAR & METAL GEAR 2: Solid Snake (Steam, Master Collection Vol. 1) | Metal Gear, Metal Gear 2: Solid Snake |
Both releases run the MSX2 games from resource files, one per language, that hold their texts, fonts, pictures and game data. The Master Collection runs them inside the engine of Bluepoint's Metal Gear Solid HD Collection, whose own files hold the scripts, layouts, textures and texts around the games.
GOG: MetalGear.exe reads Resource\<language>.bin, where <language> is the language of the [metalgear]
section of settings.ini (english by default), and falls back to Resource\English.bin. A translation can therefore
ship as a file of its own, such as Resource\polski.bin with language=polski. settings.exe only lists the five
shipped languages, so saving its dialog sets English again.
Steam: the launcher starts METAL GEAR.exe with the language chosen for the game and the region eu (jp for
Japanese). The engine takes the file lists of each stage (manifest.txt and bp_assets.txt) from the data folder of
the language, fr for English and French, gr, it, sp or jp, and loads every line of them. It reads a script
from the _bp folder beside its path when there is one there, a texture from the _win folder beside it, and each
file from the first override folder that holds it (such as ovr_stm/ and the region's ovr_eu/), down to the listed
path. In English, the games read:
| Data | Files |
|---|---|
| Metal Gear resources | fr/stage/mg1/cache/ovr_stm/ovr_eu/english.raw |
| Metal Gear 2 resources | fr/stage/mg2/cache/ovr_stm/ovr_eu/english.raw |
| Scripts | fr/stage/{mg1,mg2}/cache/_bp/00180720.gcx, assets/gcx/fr/_bp/*.gcx |
| Layouts | fr/stage/{mg1,mg2}/cache/*.la2, assets/la2/us/*.la2, assets/la2/fr/time_paradox.la2 |
| Sprite atlases | assets/tri/us/*.tri |
| Keepfont | assets/raw/us/keepfont_stage_r_sna01_r.raw |
| Textures | textures/flatlist/_win/*.ctxr |
| String tables | EngineSupport/stovc.bin (controllers), EngineSupport/stovk.bin (keyboard) |
| Sound banks | us/stage/{mg1,mg2,r_sna01}/pk0*.sdx (jp/stage/... in Japanese) |
The other copies, such as cache/english.raw and cache/00180720.gcx, are never read, and cache/ovr_stm/english.raw
only in the region us, which the launcher never passes. French, German and Italian are read from
<folder>/stage/{mg1,mg2}/cache/ovr_stm/<language>.raw, Spanish from
sp/stage/{mg1,mg2}/cache/ovr_stm/ovr_eu/spanish.raw and Japanese from jp/stage/{mg1,mg2}/cache/ovr_stm/japanese.raw.
A file that no list names is never read, so a translation replaces the files the game reads. mgtools files lists
them for any language, region and controller.
- Resource files (
resource): export the resource file of either MSX game to a folder: its texts for translation, its font as PNG images and JSON, its pictures as indexed PNG images, its palettes and sprite rectangles as JSON and the data it does not decode as.binfiles. Import checks every text against the font and the engine's limits. - GCX scripts (
gcx): export a script's code as a GCL listing to read and edit, its UTF-8 texts for translation and its glyphs as a PNG image, and compile it back. - Textures (
ctxr): convert textures to PNG images and back, remaking their mipmaps as the game's converter did. - Layouts (
la2), sprite atlases (tri) and the console font (cfon): edit them as JSON. - Fonts (
tnof,keepfont): a PNG image per glyph and JSON for the TNOF fonts (MGS_Font.raw) and keepfonts. - String tables (
stov): decryptstovc.binandstovk.binto translation documents and encrypt them back. - Sound banks (
sdx): unpack the sound effects of a stage to SPU-ADPCM waves and MIDI files, and pack them back. - Game files (
files): list which file the Master Collection reads for each line of its file lists.
Not covered: the Unity launcher of the Master Collection (launcher.exe, launcher_Data), the executables and
libraries (MetalGear.exe, settings.exe, METAL GEAR.exe, Engine.dll, mg1.dll, mg2.dll), audio codecs (the
waves stay SPU-ADPCM, the .sdt music and voice streams are not read), shaders and icons (.ico).
- Python 3.14+
Either use compiled portable build for Windows or Linux from Releases page or use your python package management system (like pipx or uv)
Example:
pipx install git+https://github.com/GrzybDev/MGTools.git
After installing the package, you can use the tools via the mgtools command (or python -m mgtools for local
installs). Every command has its own help, e.g. mgtools resource export --help.
Every group has an export and an import command. Without an output, they write next to their input, named after
it. --format picks the format of the texts for translation: xliff-2.0 (.xlf, the default), xliff-1.2 (.xliff)
or po; import tells them apart by their content.
mgtools resource info path/to/english.raw
mgtools resource export path/to/english.raw [output_dir] [--format xliff-2.0] [--charmap path/to/font.json]
mgtools resource import path/to/english [path/to/english.raw]info names the game and lists what each section holds. export writes to a new or empty folder. import writes a
.raw file unless told otherwise, so name the output of GOG files: mgtools resource import English English.bin.
The game files store no names for the letters a translation adds to the fonts. To export a translated file again, give
it the font.json of the folder it was imported from with --charmap.
- Export the file the game reads (see Files the games read) to a folder of your own:
mgtools resource export "path/to/MG and MG2/fr/stage/mg1/cache/ovr_stm/ovr_eu/english.raw" mg1 --format po. - Translate
mg1/text.poin a translation tool. Each string is a unit named after its block and number, such asmg1.b03.e000. Its notes say where the game shows it, how many bytes the original takes and which limits apply. Keep placeholders such as{wait}or{btn:1}; untranslated units keep the original text. - Give each letter the font lacks a code: name it in the
charmapofmg1/font.jsonat a code the charmap does not use (7F,A0toDF; the European releases leaveA0toA6,AEandAFfree) or instead of a letter the translation does not need. Give the code a width in thewidthsof page 0, the font of the texts, and draw the glyph inmg1/font/page0.png: each code has a cell 64 pixels wide and as high as the page, 16 a row from code20, and the glyph takes the first columns of it, in the four greys of the image. - Metal Gear draws its HUD and menu labels (
mg1.b15.e003toe008,e013toe015ande141) with an 8x8 sprite font of capitals, digits and the codesB0toDF, 8 pixels a character. Letters beyond A to Z in these labels need a code fromB0toDF, and a glyph inmg1/images/62.000.png, whose 8x8 cells go 16 a row from codeB0. - Import the folder,
mgtools resource import mg1 english.raw, and put the new file in place of the one the game reads. Import refuses texts the game cannot show and names each of them with the reason.
Japanese texts take double-byte characters too: a character the font lacks needs to be listed in the double_byte of
font.json and drawn in font/page4.png.
Import refuses what the engine cannot show:
- The game finds each string by its position: units cannot be added or removed.
- Metal Gear's message window takes 48 bytes a line, wrapped at spaces, and a blank line starts a page.
- Metal Gear 2's takes 45 bytes a line and 10 lines a page;
{wait}and{autowait}start a page. The opening story and the credits are laid out freely. - Japanese lines are never wrapped and take 48 (Metal Gear) or 47 bytes (Metal Gear 2). In Metal Gear 2,
{wait}hides the byte after it, so a line break follows it. - Every character needs a glyph: on page 0 for single bytes, on page 4 for double-byte characters.
- RGB copies of pictures can only use colours of their palette, and sprite rectangles go up to 255 pixels.
Menu strings are not wrapped either: keep them about as wide as the original, whose width in pixels their note gives.
mgtools gcx export path/to/00180720.gcx [output_dir] [--format xliff-2.0] [--source-language en]
mgtools gcx import path/to/00180720 [path/to/00180720.gcx]--source-language names the language of the texts in XLIFF documents. The scripts of the fr folder hold the French
and the English texts: the listing shows which resources (res:N) each language uses. Button icons are private-use
characters, which translation tools may show as nothing; a note names those of each unit.
mgtools ctxr export path/to/texture.ctxr [path/to/texture.png]
mgtools ctxr import path/to/texture.png [path/to/texture.ctxr]
mgtools la2 export path/to/memcard.la2 [path/to/memcard.json]
mgtools la2 import path/to/memcard.json [path/to/memcard.la2]
mgtools tri export path/to/atlas.tri [path/to/atlas.json]
mgtools cfon export path/to/console.cfon [path/to/console.json]
mgtools tnof export path/to/MGS_Font.raw [output_dir]
mgtools keepfont export path/to/keepfont.raw [output_dir]tri, cfon, tnof and keepfont import like la2, from what export wrote. A texture's JSON is written next to its
PNG image. A layout's texts are Latin-1, and import checks that its font has a glyph for each of their characters.
mgtools stov export path/to/stovc.bin [path/to/stovc.xlf] [--format xliff-2.0]
mgtools stov import path/to/stovc.xlf [path/to/stovc.bin]A table is encrypted with its name up to the first dot, so keep stovc or stovk as the name of the output. Each
string is a unit named after its id and language, such as 000e66b2.en.
mgtools sdx export path/to/pk0001a1.sdx [output_dir]
mgtools sdx import path/to/pk0001a1 [path/to/pk0001a1.sdx]mgtools files "path/to/MG and MG2" [--language en] [--region eu] [--controller xbox]Lists each line of the file lists of the language's data folder and the file the game reads for it. The region
defaults to the one the launcher passes; --keyboard-layout picks the layout of the kbd controller.
- The engine draws the UTF-8 texts of the scripts and string tables with a TNOF font built into
METAL GEAR.exe; it never readsEngineSupport/Fonts/MGS_Font.raw. That font has no Polish letters but Ó and ó, and MGTools does not patch executables. Layouts store Latin-1 texts, which lack them too. - Metal Gear's pause menu items are inside the script of section 71 of its resource file, which MGTools does not decode.
- Metal Gear 2's sprite fonts are not mapped, so its HUD labels are not checked like Metal Gear's.
- Import does not check that a script or string table keeps its button icons.
- A texture's PNG image saved without alpha imports as fully opaque.
A unit per string, named by an id that import expects back, with notes on how the game shows it. Placeholders are plain
text, never inline codes, as OmegaT 6.1 numbers the third and later inline codes of a segment wrongly. Those of the MSX
games: {wait} and {autowait} (wait for a button or half a second, then clear the window), {r} and {s} (the
speaker), {e}, {f}, {speed:N}, {bgm:NN}, {se:NN}, {btn:N} (a controller button), and {x:HH} and
{sjis:HHHH} for bytes that are no character.
- XLIFF 2.0 and 1.2: a unit (
trans-unit) per string with its notes. A non-empty target is used whatever its state, since OmegaT marks none in XLIFF 2.0, and segments a tool split are joined back. - PO: the id is the context and the notes are
#.comments. Lines are never wrapped; fuzzy translations are not used.
OmegaT 6.1 hands every .xlf and .xliff file to its legacy XLIFF filter, which only translates existing targets. In
Project > Properties... > Local File Filters..., tick Use local file filter settings and untick XLIFF (legacy
filter), or untick it for every project in Options > Global File Filters....
| Path | Holding |
|---|---|
resource.json |
The game, the containers without leaves and the indices of colours several indices show |
text.xlf |
The strings for translation (text.xliff or text.po in the other formats) |
text.json |
Metal Gear's style byte of each string and debug warp points; Metal Gear 2's string counts |
text/bNN.bin |
The text blocks that hold no strings |
font.json |
The charmap and the layout of the five font pages |
font/pageN.png |
The glyphs of each page |
images/SS.NNN.png |
The pictures, named by section and number |
palettes.json |
The palettes, as lists of #rrggbb |
actors.json |
Metal Gear: the rectangles of the actors' frames in the sprite sheets |
cells.json |
Metal Gear 2: the cell atlas of the sprite sheets |
raw/SS.NNN.bin |
Everything else, named by its position in the file |
font.json:charmapgives the character of each code from7FandA0toDFthat has one. Pages 0 to 3 (24, 12, 18 and 24 pixels high) give thewidthsof their glyphs, from 1 to 64 pixels;floor_packedmarks a page whose glyphs drop their last two pixels, which the engine draws from the next glyph, andvandpad_bitskeep the bytes the Japanese pages and some glyphs carry.double_bytelists the characters of page 4's 24x24 glyphs in order.- Pictures are indexed PNG images whose palette holds the colours the game shows, index 0 transparent and every unused index a colour of its own. Import takes the indices of such images as they are, and maps the colours of RGB and RGBA copies back to indices (alpha 0 to index 0). Metal Gear 2 changes palettes as the screen does, so its pictures show the colours of where they are drawn. The two screens of each game are RGB PNG images.
script.gcl: the listing of the code and the resources. Statements end with a semicolon; whitespace and//comments are free. Strings are in the PS2 text encoding, with codes it has no character for in hex between braces, such as{8C01}for the first glyph.text.xlf: the UTF-8 text of each string of a Bluepoint script, as unitres:N, with the PS2 text as a note where it differs.glyphs.png: the script's glyphs, 24x24 pixels, 16 a row.
- Textures: an RGBA PNG image of the full-size level, with the alpha as stored (many textures use the PS2 scale, where 128 is opaque), and JSON of the version, whether the texture has mipmaps and the texels the game's converter rounded otherwise.
- Layouts: JSON of the node tree, the items, the animations, the sprites, the colours and the fonts. Items and animations are named by their index from 0; node parents and the sprite and font of an item keep the file's indices from 1.
- Sprite atlases: JSON of each sprite's strcode and GS registers; texture coordinates follow from TEX0 and CLAMP.
- Console font: JSON of the glyph boxes in its texture and the glyph of each character.
- TNOF fonts:
font.jsonwith the metrics of each glyph and a greyscale image per glyph inglyphs/U+XXXX.png. - Keepfonts:
keepfont.jsonwith the flags of each Latin glyph (codes0x20to0xFFin the Mac OS Roman layout), its image inlatin/XX.png, 24 pixels high, and the kana inkana.png. - Sound banks:
sdx.jsonof the banks, waves and sound effects; per bank the waves aswaves/NNN.adpcm, a MIDI file per sound effect insounds/and the other chunks as.binfiles.
uv sync
uv run pytest
uv run ruff check
uv run ruff format --check
uv run mypyThe tests on the game files export and import every file of each format and expect the very same bytes. They run only when the game folders are named, and write only to pytest's temporary folders:
MGTOOLS_STEAM_DIR="path/to/MG and MG2" MGTOOLS_GOG_DIR="path/to/Metal Gear" uv run pytestSpecial thanks to:
- Konami (for publishing Metal Gear)