CATATAN
Riset struktur data dan penyisipan teks pada game River King (PS2)
Daftar isi
Catatan riset teknis mengenai struktur data, pemetaan karakter, dan proses penyisipan teks. Oktober 2026.
Pendahuluan
Menerjemahkan game lawas tidak selalu sesederhana mengambil teks bahasa Inggris, menerjemahkannya, lalu mengganti teks aslinya. Pada beberapa game konsol, teks tersimpan dalam format khusus yang harus dipahami terlebih dahulu sebelum dapat dimodifikasi dengan aman.
Hal yang sama kutemukan ketika mengerjakan proyek penerjemahan River King: A Wonderful Journey untuk PlayStation 2. Tantangannya bukan hanya menerjemahkan dialog ke bahasa Indonesia, tetapi juga memahami struktur arsip game, membongkar format file teks, memetakan kode karakter, dan memastikan teks hasil terjemahan dapat ditampilkan tanpa merusak data lainnya.
Dalam riset ini, aku mengembangkan sejumlah alat berbasis Python untuk mengekstrak, memeriksa, mengubah, dan memasukkan kembali teks ke dalam ISO game. Prosesnya juga mencakup pengujian format data agar hasil modifikasi tetap sesuai dengan struktur aslinya.
Berikut dokumentasi proses yang kulakukan, termasuk beberapa masalah yang baru diketahui setelah format datanya diperiksa lebih jauh.
1. Menemukan Lokasi Penyimpanan Teks
Langkah pertama adalah mencari lokasi penyimpanan teks di dalam game.
ISO River King: A Wonderful Journey menggunakan sistem berkas ISO9660. Di dalamnya terdapat arsip DATA0000.AFS berukuran sekitar 850 MB yang menampung berbagai data game, termasuk file teks. Sementara itu, DATA0001.AFS berukuran sekitar 160 MB berisi audio ADX, seperti musik, efek suara, dan rekaman suara.
Arsip DATA0000.AFS memiliki 2.895 file, tetapi hanya 408 file di antaranya yang teridentifikasi sebagai file teks berformat .mes.
Secara sederhana, struktur data yang kutemukan adalah sebagai berikut:
game.iso
└── DATA0000.AFS
├── 2.895 file di dalam arsip
└── 408 file .mes
└── 33.936 entri teks
Untuk mengakses data tersebut, aku membuat afs_tool.py, sebuah alat untuk membaca daftar file, mengekstrak isi arsip AFS, dan membangun kembali arsip setelah dimodifikasi.
Memahami struktur AFS
AFS merupakan format arsip yang digunakan untuk mengemas berbagai file game. Pada DATA0000.AFS, struktur dasarnya terdiri dari header AFS, jumlah file, tabel offset dan ukuran setiap file, serta tabel nama file beserta metadata.
Posisi data di dalam arsip mengikuti penyelarasan pada kelipatan 0x800.
Pembacaan tabel offset dan ukuran dapat dilakukan dengan Python menggunakan modul struct.
import struct
f = open("DATA0000.AFS", "rb")
assert f.read(4)[:3] == b"AFS"
count, = struct.unpack("<I", f.read(4))
toc = [
struct.unpack("<II", f.read(8))
for _ in range(count)
]
Potongan kode tersebut membaca jumlah file dan tabel yang berisi offset serta ukuran masing-masing file. Informasi ini menjadi dasar untuk mengekstrak file yang dibutuhkan tanpa harus memperlakukan seluruh isi arsip sebagai teks.
2. Membongkar Format File .mes
Setelah file teks berhasil ditemukan, tahap berikutnya adalah memahami struktur internalnya.
File .mes memiliki header dengan magic bytes:
B0 B0 C3 CD
Empat byte berikutnya menyimpan jumlah entri dalam format integer 32-bit. Setelah itu terdapat tabel pointer yang menunjukkan posisi setiap entri di dalam file.
Pointer pertama selalu berada pada posisi:
8 + (4 × jumlah_entri)
Struktur tersebut memungkinkan setiap entri dibaca berdasarkan posisi awal dan akhir yang ditunjukkan oleh tabel pointer.
Contoh pembacaannya:
n, = struct.unpack("<I", d[4:8])
ptrs = struct.unpack(
"<%dI" % n,
d[8:8 + 4 * n]
)
entries = [
d[s:e]
for s, e in zip(
ptrs,
list(ptrs[1:]) + [len(d)]
)
]
Namun, membaca pointer saja ternyata belum cukup untuk memastikan hasil ekstraksi benar.
Masalah byte 00
Pada percobaan awal, aku menemukan bahwa beberapa entri mengandung byte 00 di tengah data. Jika byte tersebut langsung dianggap sebagai penanda akhir teks, proses pembacaan akan memotong entri terlalu cepat.
Akibatnya, 374 dari 408 file gagal melewati pengujian roundtrip pada percobaan pertama.
Setelah diperiksa lebih jauh, byte 00 di tengah teks ternyata dapat menjadi bagian dari parameter kode kontrol. Contohnya adalah rangkaian 2E 00 yang digunakan untuk menampilkan nama pemain.
Masalah ini menunjukkan bahwa terminator teks tidak bisa ditentukan hanya dengan mencari byte nol pertama.
Setiap entri diakhiri byte 00, kemudian diberi padding nol hingga batas kelipatan empat. File .mes juga memiliki padding hingga kelipatan 16 byte.
Untuk mengambil teks, aku menggunakan pendekatan berikut:
text = entry.rstrip(b"\x00")
Dengan pendekatan ini, byte nol di bagian akhir dibuang tanpa langsung menghapus byte nol yang berada di tengah entri.
Setelah perbaikannya diterapkan, seluruh 33.936 entri berhasil melewati pengujian roundtrip tanpa kehilangan data. Pengujian tersebut memastikan bahwa data yang diekstrak dan dikonversi kembali menghasilkan byte yang identik dengan data awal.
Hasil ini penting karena proses penerjemahan tidak boleh mengubah struktur data yang sebenarnya tidak perlu diubah.
3. Memetakan Kode Karakter Game
Setelah format .mes dipahami, tantangan berikutnya adalah mengubah data teks menjadi karakter yang bisa dibaca.
Teks bahasa Inggris di dalam game tidak disimpan menggunakan ASCII biasa ataupun Shift-JIS standar. Karakter menggunakan tabel khusus buatan game, dengan sebagian besar huruf direpresentasikan oleh pasangan byte 80 XX.
Petunjuk awal ditemukan pada menu.mes, salah satu file yang memuat teks menu.
Beberapa contoh data yang berhasil diidentifikasi adalah:
10 E0 EA DF
F i s h
0F E8 EC E0 E7 E4 DC E5 EB
E q u i p m e n t
13 EB DC E4 EA
I t e m s
Pola kode yang ditemukan menunjukkan bahwa huruf yang sama menggunakan kode yang konsisten dan urutannya berderet seperti alfabet.
Berdasarkan temuan tersebut, aku mulai menyusun tabel karakter rk.tbl untuk menghubungkan kode byte dengan karakter yang sesuai.
Pengujian glyph secara langsung
Tidak semua karakter dapat dipetakan hanya dengan membaca pola byte. Untuk simbol dan tanda baca, aku membuat halaman pengujian yang menampilkan deretan kode karakter di dalam game.
Hasilnya kemudian difoto dan dicocokkan dengan posisi setiap kode.
Metode ini memungkinkan pemetaan karakter dilakukan berdasarkan tampilan aktual di game, bukan sekadar dugaan berdasarkan nilai byte.
Beberapa hasil pemetaan yang diperoleh antara lain:
| Kode | Karakter |
|---|---|
02 |
Spasi |
800B–8024 |
A–Z |
80D8–80F1 |
a–z |
8000–8009 |
0–9 |
80C8 |
Titik |
80CA |
Tanda tanya |
80CB |
Tanda seru |
80F2 |
Apostrof |
862D |
Koma |
862E |
Tanda kutip |
852C |
Titik dua |
80D0 |
Tanda hubung |
80D2 |
Garis miring |
80CF |
Titik-titik jeda |
Spasi merupakan pengecualian karena direpresentasikan oleh byte tunggal 02, bukan glyph dalam kelompok kode 80 XX.
Aku juga menemukan sejumlah karakter khusus, seperti simbol musik, bintang, lingkaran, tanda silang, dan simbol lainnya.
Meski demikian, pemetaan ini belum sepenuhnya lengkap. Beberapa kode masih belum diketahui, sedangkan sebagian tanda baca baru diidentifikasi berdasarkan konteks kalimat dan belum diuji secara langsung.
Karena itu, tabel karakter yang digunakan dalam proyek ini masih perlu dianggap sebagai hasil pemetaan yang sedang dikembangkan, bukan dokumentasi final seluruh font game.
4. Memahami Kode Kontrol dalam Dialog
Selain karakter biasa, teks game mengandung kode kontrol yang menentukan bagaimana dialog ditampilkan.
Kode-kode tersebut harus dipertahankan ketika teks diterjemahkan. Jika dihapus atau diubah secara tidak sengaja, dialog dapat kehilangan format, pergantian baris, atau fungsi tertentu.
Berikut beberapa kode yang berhasil diidentifikasi.
| Kode | Fungsi | Status |
|---|---|---|
01 |
Pindah baris | Terbukti |
30 |
Akhir pesan atau menunggu tombol | Terbukti |
30 31 |
Pergantian halaman | Terbukti dari pola |
12–17 |
Pengaturan warna teks | Sebagian telah dikonfirmasi |
2E 00 |
Menampilkan nama pemain | Terbukti |
32 00 C8 |
Menampilkan teks secara perlahan | Dikonfirmasi melalui pengujian |
29 00 |
Diduga berkaitan dengan nama tokoh lain | Dugaan |
2A 00 NN |
Diduga berkaitan dengan nama tokoh lain dan angka digit | Dugaan |
40, 41 |
Diduga sebagai penanda pilihan | Dugaan |
Tidak semua kode memiliki fungsi yang sudah dipastikan. Beberapa masih berupa hipotesis berdasarkan konteks penggunaannya, sehingga tidak boleh diperlakukan sebagai fakta final.
Mengapa kode kontrol harus dipertahankan?
Dalam proses penerjemahan, teks yang terbaca manusia bukan satu-satunya bagian yang penting. Kode kontrol juga merupakan bagian dari struktur dialog.
Sebagai contoh, 2E 00 harus tetap dipertahankan jika game menggunakannya untuk menampilkan nama pemain. Begitu pula kode pergantian halaman yang menentukan kapan pemain harus menekan tombol untuk melanjutkan dialog.
Untuk meminimalkan kesalahan, aku membuat alat pemeriksaan yang dapat mendeteksi kode {..} yang hilang atau jumlahnya berubah setelah proses penerjemahan.
5. Mengatasi Batas Kotak Dialog
Setelah masalah struktur data dan encoding mulai teratasi, muncul persoalan lain: teks bahasa Indonesia lebih panjang daripada teks bahasa Inggris.
Terjemahan yang secara linguistik sudah benar belum tentu bisa ditampilkan dengan baik di dalam game. Jika terlalu panjang, teks dapat melampaui batas kotak dialog atau bertabrakan dengan kotak nama karakter.
Berdasarkan pengamatan terhadap tampilan game, font terlihat memiliki lebar karakter yang relatif tetap, dengan perkiraan sekitar 18 piksel per karakter pada tangkapan layar. Kotak dialog diperkirakan mampu menampung sekitar 33 karakter dalam satu baris.
Namun, untuk kebutuhan penerjemahan, aku menggunakan batas praktis yang lebih konservatif:
- Baris pertama: maksimal 28 karakter.
- Baris kedua: maksimal 28 karakter.
- Baris ketiga: maksimal 22 karakter.
Batas ini merupakan aturan kerja berdasarkan pengamatan tampilan game, bukan ukuran maksimum universal yang telah dibuktikan untuk seluruh situasi.
Perbedaan posisi kotak nama
Tata letak dialog juga berbeda berdasarkan jenis tokoh.
Pada dialog anggota keluarga, avatar dan kotak nama berada di sebelah kiri. Akibatnya, sebagian ruang di baris ketiga tertutup oleh kotak nama karakter.
Untuk menghindari tabrakan, baris ketiga perlu diawali sekitar enam hingga delapan spasi.
Sebaliknya, pada NPC lain, kotak nama berada di sebelah kanan sehingga baris ketiga tidak memerlukan padding spasi di bagian awal.
Aturan tersebut menjadi bagian dari proses penataan teks otomatis.
Pemotongan baris dan pergantian halaman
Aku membuat rapikan.py untuk membantu memotong baris dan mengatur pergantian halaman secara otomatis.
Aturan yang diterapkan meliputi:
- Pemotongan baris dilakukan pada spasi agar kata tidak terbelah.
- Baris dapat diakhiri pada tanda akhir kalimat yang sesuai.
- Jika teks tidak muat pada baris ketiga, sisanya dipindahkan ke kotak dialog berikutnya.
- Padding untuk dialog anggota keluarga dipertahankan sesuai kebutuhan tata letak.
Sebagai contoh, potongan dialog berikut menunjukkan bagaimana teks dibagi menjadi beberapa baris dan halaman:
{2E}{00}, kamu nggak punya PR{01}
yang harus dikerjakan? Nanti{01}
jangan nangis di{30}{31}{01}
menit-menit terakhir karena{01}
kamu terus menundanya selama{01}
ini!{30}
Pada contoh tersebut, kode {2E}{00} digunakan untuk menampilkan nama pemain, sedangkan {01} memisahkan baris dan {30}{31} digunakan untuk pergantian halaman.
Spasi di awal baris ketiga bukan kesalahan format. Spasi tersebut merupakan bagian dari penyesuaian tata letak untuk menghindari kotak nama karakter.
Penataan seperti ini penting karena hasil penerjemahan harus tetap terbaca dan sesuai dengan tampilan aslinya, bukan hanya lolos pemeriksaan struktur data.
6. Membangun Alur Penerjemahan
Setelah format teks, tabel karakter, dan kode kontrol mulai dipahami, aku menyusun alur kerja untuk memproses teks secara lebih efisien.
Dari 33.936 entri yang ditemukan, sekitar 5.886 teks unik dikumpulkan ke dalam master.txt. Entri lainnya mencakup teks duplikat, angka, simbol, dan sisa teks Jepang yang tidak dimasukkan ke dalam alur penerjemahan utama.
Pemisahan teks unik dilakukan agar teks yang sama tidak perlu dikerjakan berulang kali.
Alur kerjanya secara umum adalah:
game.iso
│
▼
DATA0000.AFS
│
▼
Ekstraksi file .mes
│
▼
Dekode menggunakan rk.tbl
│
▼
txt_asli
│
▼
Pengumpulan teks unik
│
▼
master.txt
│
▼
Penerjemahan dan penataan teks
│
▼
Pemeriksaan kode kontrol
│
▼
Penyisipan kembali ke file .mes
│
▼
Build AFS dan penggantian di ISO
│
▼
Pengujian hasil
Dalam proses penerjemahan, terdapat beberapa aturan yang digunakan:
- Nama ikan, tempat, dan item tetap menggunakan nama bahasa Inggris aslinya.
- Sapaan
FatherdanMotherditerjemahkan menjadiAyahdanIbu. - Semua kode kontrol
{..}harus tetap utuh. - Karakter hasil terjemahan harus tersedia di tabel karakter game.
- Panjang teks harus disesuaikan dengan batas praktis kotak dialog.
Untuk memastikan aturan tersebut tidak hanya bergantung pada pemeriksaan manual, aku membuat beberapa skrip validasi.
Alat yang dikembangkan
| Alat | Fungsi |
|---|---|
afs_tool.py |
Membaca, mengekstrak, dan membangun arsip AFS |
iso_tool.py |
Mengganti file di dalam ISO9660 tanpa menggeser file lain secara sembarangan |
mes_tool.py |
Mengekstrak, menyisipkan, dan menguji roundtrip file .mes |
rk.tbl |
Menyimpan pemetaan kode karakter game |
mes_unique.py |
Mengumpulkan teks unik, membuat master.txt dan non_master.txt, serta menerapkan perubahan |
mes_check.py |
Memeriksa kode kontrol dan batas lebar baris |
mes_fix.py |
Membantu memeriksa dan memperbaiki masalah kode kontrol |
rapikan.py |
Memotong baris dan mengatur pergantian halaman secara otomatis |
rakit.sh |
Menjalankan rangkaian proses penerapan terjemahan, pembangunan AFS, penggantian ISO, dan pemeriksaan |
Untuk penggantian file di ISO, iso_tool.py menangani kondisi ketika ukuran file hasil modifikasi lebih besar daripada file aslinya. Jika file tidak dapat ditulis pada lokasi semula karena keterbatasan ruang, file dapat dipindahkan ke bagian akhir ISO.
Sementara itu, rakit.sh menyatukan beberapa langkah menjadi satu perintah agar proses penerapan perubahan dan pemeriksaan tidak perlu dilakukan secara terpisah setiap kali.
7. Masalah yang Ditemukan dan Cara Mengatasinya
Selama proses pengembangan, beberapa masalah baru diketahui setelah data diuji secara langsung.
| Masalah | Penyebab | Penanganan |
|---|---|---|
| Roundtrip gagal pada 374 dari 408 file | Byte 00 di tengah teks dan penanganan padding yang keliru |
Memperbaiki cara menentukan akhir entri dan mempertahankan data yang tidak diubah |
| Teks keluar dari kotak dialog | Panjang terjemahan melampaui ruang yang tersedia | Membatasi lebar baris dan memindahkan teks ke halaman berikutnya |
| Baris ketiga bertabrakan dengan kotak nama | Posisi kotak nama menutupi sebagian area dialog | Menambahkan padding untuk dialog anggota keluarga |
73 entri kehilangan kode {..} |
Kode kontrol terhapus atau berubah selama penerjemahan | Menambahkan pemeriksaan dan perbaikan kode kontrol |
| Ukuran ISO bertambah | File hasil modifikasi lebih besar daripada data sebelumnya | Menangani penempatan file melalui iso_tool.py |
Temuan-temuan tersebut memperlihatkan bahwa proses ROM hacking tidak hanya bergantung pada kemampuan mengekstrak data. Keakuratan pembacaan format, konsistensi struktur, dan pemeriksaan hasil juga menentukan apakah modifikasi dapat diterapkan dengan aman.
Satu kesalahan kecil dalam menentukan terminator, misalnya, dapat memengaruhi banyak file sekaligus. Karena itu, pemeriksaan roundtrip dan validasi kode kontrol menjadi bagian penting dalam alur kerja, bukan sekadar langkah tambahan.
8. Hasil dan Keterbatasan Riset
Pada tahap riset ini, beberapa bagian telah berhasil dikerjakan.
Hasil yang telah dicapai:
- Menemukan 408 file
.mesdi dalam arsipDATA0000.AFS. - Mengidentifikasi total 33.936 entri teks.
- Mengembangkan proses ekstraksi dan penyisipan kembali teks.
- Memetakan sebagian besar karakter yang diperlukan untuk teks bahasa Inggris dan penerjemahan.
- Mengidentifikasi sejumlah kode kontrol melalui pemeriksaan data dan pengujian dalam game.
- Memperbaiki proses roundtrip hingga seluruh entri menghasilkan byte yang identik dengan data awal.
- Mengembangkan alat untuk memeriksa kode kontrol, mengatur lebar teks, dan membangun kembali ISO.
- Menguji hasil modifikasi pada sejumlah dialog di emulator.
Meskipun demikian, masih terdapat beberapa keterbatasan.
Pertama, beberapa kode karakter belum berhasil dipetakan, termasuk 800A, 81BE, dan 826C. Pemetaan sejumlah tanda baca juga masih didasarkan pada konteks kalimat, belum melalui pengujian langsung.
Kedua, teks yang tersimpan sebagai gambar, seperti judul, ikon menu, dan layar input nama, berada di aset tekstur .tex atau .tmb dan belum dimodifikasi.
Ketiga, entri yang berkaitan dengan kode pilihan 40 dan 41, serta teks Jepang yang tersisa, belum diubah dalam proses penerjemahan utama.
Terakhir, pengujian baru dilakukan pada sejumlah dialog di emulator. Game belum diuji secara menyeluruh dari awal sampai akhir, sehingga belum dapat dipastikan bahwa seluruh teks hasil modifikasi akan tampil dengan benar di semua situasi.
Dengan kata lain, keberhasilan ekstraksi dan roundtrip merupakan hasil yang sudah diuji, tetapi cakupan keberhasilan penerjemahan dan kompatibilitas seluruh game masih memerlukan pengujian lanjutan.
Penutup
Riset penerjemahan River King: A Wonderful Journey menunjukkan bahwa memahami format data merupakan bagian penting dari proses menerjemahkan game konsol lawas.
Tantangan awalnya adalah menemukan teks di dalam arsip AFS dan memahami struktur file .mes. Setelah itu, diperlukan pemetaan karakter khusus, identifikasi kode kontrol, serta penanganan batas kotak dialog agar teks bahasa Indonesia dapat ditampilkan dengan benar.
Proses ini juga memperlihatkan pentingnya pengujian berulang. Kegagalan roundtrip pada ratusan file awalnya bukan disebabkan oleh teks yang rusak, melainkan oleh cara pembacaan terminator dan padding yang belum tepat. Setelah logika tersebut diperbaiki, seluruh 33.936 entri berhasil melewati pengujian tanpa kehilangan data.
Sejumlah alat yang dikembangkan selama riset akhirnya membentuk alur kerja yang mencakup ekstraksi, pengolahan teks, validasi, penyisipan kembali, dan pengujian hasil.
Pekerjaan ini belum mencakup seluruh aset visual dan belum diuji melalui permainan penuh. Namun, hasil yang sudah diperoleh menjadi dasar teknis untuk melanjutkan penerjemahan dan menyempurnakan kompatibilitasnya.
Dokumentasi ini juga menjadi catatan tentang proses pemecahan masalah dalam ROM hacking: mulai dari membaca struktur data mentah, menguji dugaan, menemukan penyebab kegagalan, hingga membangun alat yang lebih sesuai dengan format game yang sedang dikerjakan.