Skip to content

Commit ec174c8

Browse files
committed
inital upload
1 parent a3ce85a commit ec174c8

File tree

16 files changed

+942
-1
lines changed

16 files changed

+942
-1
lines changed

.github/workflows/build-linux.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Linux Nightly
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
build:
14+
name: Build Linux nightly
15+
runs-on: ubuntu-24.04
16+
timeout-minutes: 15
17+
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 1
23+
24+
- name: Install toolchain
25+
run: |
26+
set -euo pipefail
27+
sudo apt update
28+
sudo apt install -y --no-install-recommends \
29+
clang \
30+
make
31+
32+
- name: Build Linux binaries
33+
run: |
34+
set -euo pipefail
35+
make CC=clang
36+
37+
- name: Upload ra2mes Linux build artefact
38+
uses: actions/upload-artifact@v4
39+
with:
40+
name: ra2mes-linux
41+
path: ra2mes
42+
if-no-files-found: error
43+
44+
- name: Upload ra3mes Linux build artefact
45+
uses: actions/upload-artifact@v4
46+
with:
47+
name: ra3mes-linux
48+
path: ra3mes
49+
if-no-files-found: error
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Windows Nightly
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
build:
14+
name: Build Windows nightly
15+
runs-on: windows-2025
16+
timeout-minutes: 15
17+
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 1
23+
24+
- name: Install toolchain
25+
run: |
26+
choco install -y mingw make
27+
28+
- name: Build Windows binaries
29+
shell: bash
30+
run: |
31+
set -euo pipefail
32+
make
33+
34+
- name: Upload ra2mes Windows build artefact
35+
uses: actions/upload-artifact@v4
36+
with:
37+
name: ra2mes-windows
38+
path: ra2mes.exe
39+
if-no-files-found: error
40+
41+
- name: Upload ra3mes Windows build artefact
42+
uses: actions/upload-artifact@v4
43+
with:
44+
name: ra3mes-windows
45+
path: ra3mes.exe
46+
if-no-files-found: error

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
*.i*86
3636
*.x86_64
3737
*.hex
38+
ra2msg
39+
ra3msg
3840

3941
# Debug files
4042
*.dSYM/

Makefile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
CC := $(shell command -v clang >/dev/null 2>&1 && echo clang || echo gcc)
2+
CFLAGS := -O3 -Wall -Wextra -Werror
3+
4+
ifeq ($(OS),Windows_NT)
5+
EXT := .exe
6+
else
7+
EXT :=
8+
endif
9+
10+
RA2_TARGET := ra2mes$(EXT)
11+
RA3_TARGET := ra3mes$(EXT)
12+
13+
COMMON_SRCS := utils.c
14+
RA2_SRCS := ra2mes.c $(COMMON_SRCS)
15+
RA3_SRCS := ra3mes.c $(COMMON_SRCS)
16+
17+
RA2_OBJS := $(RA2_SRCS:.c=.o)
18+
RA3_OBJS := $(RA3_SRCS:.c=.o)
19+
20+
.PHONY: all clean
21+
22+
all: $(RA2_TARGET) $(RA3_TARGET)
23+
24+
$(RA2_TARGET): $(RA2_OBJS)
25+
$(CC) -o $@ $^
26+
27+
$(RA3_TARGET): $(RA3_OBJS)
28+
$(CC) -o $@ $^
29+
30+
utils.o: utils.c utils.h
31+
$(CC) $(CFLAGS) -c $< -o $@
32+
33+
ra2mes.o: ra2mes.c ra2mes.h utils.h
34+
$(CC) $(CFLAGS) -c $< -o $@
35+
36+
ra3mes.o: ra3mes.c ra3mes.h utils.h
37+
$(CC) $(CFLAGS) -c $< -o $@
38+
39+
clean:
40+
$(RM) $(RA2_OBJS) $(RA3_OBJS) $(RA2_TARGET) $(RA3_TARGET)

README.md

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,66 @@
11
# ra23mes
2-
MES text file converter for Pokémon Ranger: Shadows of Almia and Pokémon Ranger: Guardian Signs.
2+
<a href="https://github.com/SombrAbsol/ra23mes/actions/workflows/build-linux.yml"><img src="https://github.com/SombrAbsol/ra23mes/actions/workflows/build-linux.yml/badge.svg" alt="Linux Nightly"></a>
3+
<a href="https://github.com/SombrAbsol/ra23mes/actions/workflows/build-windows.yml"><img src="https://github.com/SombrAbsol/ra23mes/actions/workflows/build-windows.yml/badge.svg" alt="Windows Nightly"></a>
4+
<a href="https://opensource.org/license/mit"><img src="https://img.shields.io/badge/license-MIT-blue" alt="License: MIT (Expat)"></a>
5+
6+
MES text file converters for *Pokémon Ranger: Shadows of Almia* and *Pokémon Ranger: Guardian Signs*.
7+
8+
MES files are used to store texts in these two games. *Pokémon Ranger: Shadows of Almia* is the first game to use it, while *Pokémon Ranger: Guardian Signs* use a revised specification of this format, with different text storage and control characters.
9+
10+
For more information on the MES format, see [the documentation](/doc/mes.md).
11+
12+
## Download
13+
| | Linux | Windows |
14+
| ------- | ----- | ------- |
15+
| Release | | |
16+
| Nightly | [ra2mes](https://nightly.link/SombrAbsol/ra23mes/workflows/build-linux/main/ra2mes-linux.zip)<br>[ra3mes](https://nightly.link/SombrAbsol/ra23mes/workflows/build-linux/main/ra3mes-linux.zip) | [ra2mes](https://nightly.link/SombrAbsol/ra23mes/workflows/build-windows/main/ra2mes-windows.zip)<br>[ra3mes](https://nightly.link/SombrAbsol/ra23mes/workflows/build-windows/main/ra3mes-windows.zip) |
17+
18+
## Usage
19+
### Dumping the ROM
20+
You can dump your own *Pokémon Ranger: Guardian Signs* ROM from:
21+
* [a console from the Nintendo DS or Nintendo 3DS family](https://dumping.guide/carts/nintendo/ds) (Game Card release)
22+
* [a Wii U](https://wiki.hacks.guide/wiki/Wii_U:VC_Extract) (Virtual Console release)
23+
24+
### Getting the MES files
25+
To get the MES files, Windows users can run [TinkeDSi](https://github.com/R-YaTian/TinkeDSi), but [NDSFactory](https://github.com/Luca1991/NDSFactory) can also be run on macOS and Linux:
26+
1. Download [the latest NDSFactory release](https://github.com/Luca1991/NDSFactory/releases/latest), then extract the archive and run the executable
27+
2. Open the program, load your ROM, then scroll down until you see the `FAT Files Address` field. Take note or copy its value
28+
3. Press the `Extract Everything` button and choose where to save your files
29+
4. Once the process is complete, go to the `Fat Tools` tab, and fill in the first three fields with the requested files you just extracted (`fat_data.bin`, `fnt.bin` and `fat.bin`) and the fourth with the value from the previous `FAT Files Address` field
30+
5. Press the `Extract FAT Data!` button and choose where to save your files
31+
32+
Go to your output directory. MES files location is different depending of the game:
33+
* *Pokémon Ranger: Shadows of Almia*: in the `data/message` directory
34+
* *Pokémon Ranger: Guardian Signs*: inside the `data_localize` ACF archives in the `data` directory. These are the first 275 (277 in the USA Kiosk Demo) files in the archives, by default with the extension `.bin`. You will need [acftool](https://github.com/SombrAbsol/acftool) to extract them
35+
36+
### Running ra2mes and ra3mes
37+
Please use ra2mes for *Pokémon Ranger: Shadows of Almia*'s MES files only, and ra3mes for *Pokémon Ranger: Guardian Signs*' MES files only.
38+
39+
* To convert a MES file to a JSON file, run `ra2mes --to-json in.mes` or `ra3mes --to-json in.mes`
40+
* To convert a JSON file to a MES file, run `ra2mes --to-mes in.json` or `ra3mes --to-mes in.json`
41+
42+
By default, the output file has the same name as the input file, with the extension `.json` or `.mes` depending on the conversion option specified. However, you can optionally specify the output name, for instance:
43+
* `ra2mes --to-json in.mes out.json` will output the JSON file as `out.json`
44+
* `ra3mes --to-mes in.json out.mes` will output the MES file as `out.mes`
45+
46+
## Building
47+
Dependencies: `clang` or `gcc`, `make` (optional, preferred)
48+
1. Clone this repository by running `git clone https://github.com/SombrAbsol/ra23mes`, or [download the ZIP archive](https://github.com/SombrAbsol/ra23mes/archive/refs/heads/main.zip) and extract it
49+
2. Go to the repository directory and build the project. You can run `make` if you have it installed, or the following commands depending on the compiler you have installed.
50+
### Clang
51+
```
52+
clang -O3 -Wall -Wextra -Werror -o ra2mes ra2mes.c utils.c
53+
clang -O3 -Wall -Wextra -Werror -o ra3mes ra3mes.c utils.c
54+
```
55+
56+
### GCC
57+
```
58+
gcc -O3 -Wall -Wextra -Werror -o ra2mes ra2mes.c utils.c
59+
gcc -O3 -Wall -Wextra -Werror -o ra3mes ra3mes.c utils.c
60+
```
61+
62+
## Credits
63+
ra2mes and ra3mes by [SombrAbsol](https://github.com/SombrAbsol).
64+
65+
## License
66+
ra2mes and ra3mes are free softwares. You can redistribute them and/or modify them under the [terms of the Expat License](/LICENSE) as published by the Massachusetts Institute of Technology.

doc/mes.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# MES File Format
2+
Used to store texts in:
3+
* *Pokémon Ranger: Shadows of Almia* (version 1)
4+
* *Pokémon Ranger: Guardian Signs* (version 2)
5+
6+
## Format specifications
7+
### Common
8+
* 32-bit little-endian integer values
9+
* Encoded as raw byte strings
10+
* Always null-terminated
11+
* Followed by zero padding so that the total size is a multiple of 4 bytes
12+
13+
### Pokémon Ranger: Shadows of Almia
14+
Use version 1 of the MES format, where strings are stored sequentially, each preceded by a block size. They are read in order; offsets are implicit.
15+
```rust
16+
{
17+
u32 total_size // total file size in bytes
18+
u32 count // number of strings
19+
}
20+
```
21+
22+
Repeat count times:
23+
```rust
24+
{
25+
u32 block_size // string length + null terminator + padding bytes
26+
char string[] // null-terminated
27+
// zero padding bytes
28+
}
29+
```
30+
31+
### Pokémon Ranger: Guardian Signs
32+
Use version 2 of the MES format, where strings are stored in a data section and referenced sequentially via an offset table.
33+
```rust
34+
{
35+
u32 total_size // total file size in bytes
36+
u32 count // number of strings
37+
}
38+
```
39+
40+
Offset table begins at byte `0x08` and count entries. Offsets are absolute, relative to the start of the file.
41+
```rust
42+
{
43+
u32 offset[0]
44+
u32 offset[1]
45+
// ...
46+
u32 offset[n-1]
47+
}
48+
```
49+
50+
String data, repeated for each string:
51+
```rust
52+
{
53+
char string[] // null-terminated
54+
// zero padding bytes
55+
}
56+
```
57+
58+
## List of control characters
59+
Unless constants are specified, X is a numeric variable, whose action associated with its value (usually 0, then 1, 2, etc. if necessary) is assigned on the fly in the game code. For instance, if the game decides to set `[P:0]` to Pikachu, then using `[P:0]` in the text displayed at that moment will display the name "Pikachu". Another instance: the text color is a constant, so using `[C:5]` in the text will always output green text.
60+
61+
### Pokémon Ranger: Shadows of Almia
62+
* `[B]` Big text
63+
* `[C:X]` Text color
64+
* `0` Transparent
65+
* `1` White
66+
* `2` Black (default)
67+
* `3` Red
68+
* `4` Blue
69+
* `5` Green
70+
* `[E]` Line break
71+
* `[F:X]` Move name
72+
* `[K:X]` Number
73+
* `[M]` Player name
74+
* `[N:X]` Pokémon encounter text
75+
* `0` Empty
76+
* `1` "The wild" (default)
77+
* `2` "attacked!"
78+
* `[O:XX]` Display image
79+
* `04` Styler Menu button icon
80+
* `05` Change Screen button icon
81+
* `[P:X]` Pokémon name
82+
* `[Q]` Yes/No question
83+
* `[R]` New dialog page after button press
84+
* `[V:X]` Play Pokémon cry
85+
* `[W:XX]` Text speed
86+
* `00` Default
87+
* `01` Fast
88+
* `03` A bit slow
89+
* Slower as the value increases
90+
* `60` Slowest speed used in-game
91+
* `[Y:X]` Target name
92+
93+
### Pokémon Ranger: Guardian Signs
94+
* `[C:X]` Text color
95+
* `0` White (default)
96+
* `1` Orange in Ranger Net menu, white elsewhere
97+
* `2` Black
98+
* `3` Red
99+
* `4` Blue
100+
* `5` Green
101+
* `[D]` Resets text speed to default, remove first character on the right
102+
* `[D:]` Resets text speed to default
103+
* `[E]` Line break
104+
* `[F:X]` Destination name
105+
* `[K:X]` Number
106+
* `[M]` Player name, remove first character on the right
107+
* `[M:]` Player name
108+
* `[N:X]` Player's counterpart name
109+
* `[O:XX]` Display image
110+
* `00` Speech bubble with ellipsis
111+
* `01` Cut Field Move icon
112+
* `02` Recharge Field Move icon
113+
* `03` Fire Group icon
114+
* `04` Water Group icon
115+
* `05` Grass Group icon
116+
* `06` Electric Group icon
117+
* `07` Ground Group icon
118+
* `[P:X]` Pokémon name
119+
* `[Q]` Yes/No question (auto skip text if no answer is defined)
120+
* `[Q:]` Yes/No question (auto skip text if no answer is defined), remove first character on the right
121+
* `[R]` New dialog page after button press
122+
* `[S:XX]` Text speed, testing only and non-functional
123+
* `[T:X]` Target name
124+
* `[W:XX]` Text speed
125+
* `00` Instant
126+
* `01` Fast
127+
* `02` Normal (default)
128+
* Slower as the value increases
129+
* `90` Slowest speed used in-game

example/example-ra2.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"000": "NONE",
3+
"001": "[W:01][C:4]James[C:2][W:00]:[E]Hey guys[W:30],[W:00] the [C:5]old treasure meter[C:2]'s[E]going off the charts!",
4+
"002": "[W:01][C:3]Jessie[C:2][W:00]:[E]It won't be long now.[R]We'll clean up in more ways than one.",
5+
"003": "[W:01][C:3]Jessie[C:2][W:00]:[E]That old[W:30],[W:00] crusty crab thinks[E]he's so smart[W:30],[W:00] when we're the ones[E]poised to pocket those [C:4]diamonds[C:2] and [C:3]pearls[C:2].",
6+
"004": "[W:01][C:3]Jessie[C:2][W:00]:[E]Hey[W:30],[W:00] that sounds like a [C:5]good name[E]for a game[C:2]!",
7+
"005": "[B][C:3][W:30]Wo-[W:00]Wobbuffet![C:2]"
8+
}

example/example-ra2.mes

580 Bytes
Binary file not shown.

example/example-ra3.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"000": "NONE",
3+
"001": "Hey guys[W:30],[D:] the [C:5]old treasure meter[C:0]'s[E]going off the charts!",
4+
"002": "It won't be long now.[R]We'll clean up in more ways than one.",
5+
"003": "That old[W:30],[D:] crusty crab thinks[E]he's so smart[W:30],[D:] when we're the ones[E]poised to pocket those [C:4]diamonds[C:0] and [C:3]pearls[C:0].",
6+
"004": "Hey[W:30],[D:] that sounds like a [C:5]good name for a game[C:0]!",
7+
"005": "[C:3][W:30]Wo-[D:]Wobbuffet![C:0]"
8+
}

example/example-ra3.mes

440 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)