This page hosts a formal specification of .nes file format using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
All parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.
For C++, the easiest way is to clone the runtime library sources and build them along with your project.
Using Kaitai Struct in C++/STL usually consists of 3 steps.
std::istream
). One can open local file for that, or use existing std::string
or char*
buffer.
#include <fstream>
std::ifstream is("path/to/local/file.nes", std::ifstream::binary);
#include <sstream>
std::istringstream is(str);
#include <sstream>
const char buf[] = { ... };
std::string str(buf, sizeof buf);
std::istringstream is(str);
#include "kaitai/kaitaistream.h"
kaitai::kstream ks(&is);
ines_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.header() // => get header
#pragma once
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <memory>
#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
/**
* \sa https://www.nesdev.org/wiki/INES Source
*/
class ines_t : public kaitai::kstruct {
public:
class header_t;
class playchoice10_t;
ines_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, ines_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~ines_t();
class header_t : public kaitai::kstruct {
public:
class f6_t;
class f7_t;
class f9_t;
class f10_t;
header_t(kaitai::kstream* p__io, ines_t* p__parent = nullptr, ines_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~header_t();
/**
* \sa https://www.nesdev.org/wiki/INES#Flags_6 Source
*/
class f6_t : public kaitai::kstruct {
public:
enum mirroring_t {
MIRRORING_HORIZONTAL = 0,
MIRRORING_VERTICAL = 1
};
f6_t(kaitai::kstream* p__io, ines_t::header_t* p__parent = nullptr, ines_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~f6_t();
private:
uint64_t m_lower_mapper;
bool m_four_screen;
bool m_trainer;
bool m_has_battery_ram;
mirroring_t m_mirroring;
ines_t* m__root;
ines_t::header_t* m__parent;
public:
/**
* Lower nibble of mapper number
*/
uint64_t lower_mapper() const { return m_lower_mapper; }
/**
* Ignore mirroring control or above mirroring bit; instead provide four-screen VRAM
*/
bool four_screen() const { return m_four_screen; }
/**
* 512-byte trainer at $7000-$71FF (stored before PRG data)
*/
bool trainer() const { return m_trainer; }
/**
* If on the cartridge contains battery-backed PRG RAM ($6000-7FFF) or other persistent memory
*/
bool has_battery_ram() const { return m_has_battery_ram; }
/**
* if 0, horizontal arrangement. if 1, vertical arrangement
*/
mirroring_t mirroring() const { return m_mirroring; }
ines_t* _root() const { return m__root; }
ines_t::header_t* _parent() const { return m__parent; }
};
/**
* \sa https://www.nesdev.org/wiki/INES#Flags_7 Source
*/
class f7_t : public kaitai::kstruct {
public:
f7_t(kaitai::kstream* p__io, ines_t::header_t* p__parent = nullptr, ines_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~f7_t();
private:
uint64_t m_upper_mapper;
uint64_t m_format;
bool m_playchoice10;
bool m_vs_unisystem;
ines_t* m__root;
ines_t::header_t* m__parent;
public:
/**
* Upper nibble of mapper number
*/
uint64_t upper_mapper() const { return m_upper_mapper; }
/**
* If equal to 2, flags 8-15 are in NES 2.0 format
*/
uint64_t format() const { return m_format; }
/**
* Determines if it made for a Nintendo PlayChoice-10 or not
*/
bool playchoice10() const { return m_playchoice10; }
/**
* Determines if it is made for a Nintendo VS Unisystem or not
*/
bool vs_unisystem() const { return m_vs_unisystem; }
ines_t* _root() const { return m__root; }
ines_t::header_t* _parent() const { return m__parent; }
};
/**
* \sa https://www.nesdev.org/wiki/INES#Flags_9 Source
*/
class f9_t : public kaitai::kstruct {
public:
enum tv_system_t {
TV_SYSTEM_NTSC = 0,
TV_SYSTEM_PAL = 1
};
f9_t(kaitai::kstream* p__io, ines_t::header_t* p__parent = nullptr, ines_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~f9_t();
private:
uint64_t m_reserved;
tv_system_t m_tv_system;
ines_t* m__root;
ines_t::header_t* m__parent;
public:
uint64_t reserved() const { return m_reserved; }
/**
* if 0, NTSC. If 1, PAL.
*/
tv_system_t tv_system() const { return m_tv_system; }
ines_t* _root() const { return m__root; }
ines_t::header_t* _parent() const { return m__parent; }
};
/**
* \sa https://www.nesdev.org/wiki/INES#Flags_10 Source
*/
class f10_t : public kaitai::kstruct {
public:
enum tv_system_t {
TV_SYSTEM_NTSC = 0,
TV_SYSTEM_DUAL1 = 1,
TV_SYSTEM_PAL = 2,
TV_SYSTEM_DUAL2 = 3
};
f10_t(kaitai::kstream* p__io, ines_t::header_t* p__parent = nullptr, ines_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~f10_t();
private:
uint64_t m_reserved1;
bool m_bus_conflict;
bool m_prg_ram;
uint64_t m_reserved2;
tv_system_t m_tv_system;
ines_t* m__root;
ines_t::header_t* m__parent;
public:
uint64_t reserved1() const { return m_reserved1; }
/**
* If 0, no bus conflicts. If 1, bus conflicts.
*/
bool bus_conflict() const { return m_bus_conflict; }
/**
* If 0, PRG ram is present. If 1, not present.
*/
bool prg_ram() const { return m_prg_ram; }
uint64_t reserved2() const { return m_reserved2; }
/**
* if 0, NTSC. If 2, PAL. If 1 or 3, dual compatible.
*/
tv_system_t tv_system() const { return m_tv_system; }
ines_t* _root() const { return m__root; }
ines_t::header_t* _parent() const { return m__parent; }
};
private:
bool f_mapper;
int32_t m_mapper;
public:
/**
* \sa https://www.nesdev.org/wiki/Mapper Source
*/
int32_t mapper();
private:
std::string m_magic;
uint8_t m_len_prg_rom;
uint8_t m_len_chr_rom;
std::unique_ptr<f6_t> m_f6;
std::unique_ptr<f7_t> m_f7;
uint8_t m_len_prg_ram;
std::unique_ptr<f9_t> m_f9;
std::unique_ptr<f10_t> m_f10;
std::string m_reserved;
ines_t* m__root;
ines_t* m__parent;
std::string m__raw_f6;
std::unique_ptr<kaitai::kstream> m__io__raw_f6;
std::string m__raw_f7;
std::unique_ptr<kaitai::kstream> m__io__raw_f7;
std::string m__raw_f9;
std::unique_ptr<kaitai::kstream> m__io__raw_f9;
std::string m__raw_f10;
std::unique_ptr<kaitai::kstream> m__io__raw_f10;
public:
std::string magic() const { return m_magic; }
/**
* Size of PRG ROM in 16 KB units
*/
uint8_t len_prg_rom() const { return m_len_prg_rom; }
/**
* Size of CHR ROM in 8 KB units (Value 0 means the board uses CHR RAM)
*/
uint8_t len_chr_rom() const { return m_len_chr_rom; }
f6_t* f6() const { return m_f6.get(); }
f7_t* f7() const { return m_f7.get(); }
/**
* Size of PRG RAM in 8 KB units (Value 0 infers 8 KB for compatibility; see PRG RAM circuit on nesdev.com)
*/
uint8_t len_prg_ram() const { return m_len_prg_ram; }
f9_t* f9() const { return m_f9.get(); }
/**
* this one is unofficial
*/
f10_t* f10() const { return m_f10.get(); }
std::string reserved() const { return m_reserved; }
ines_t* _root() const { return m__root; }
ines_t* _parent() const { return m__parent; }
std::string _raw_f6() const { return m__raw_f6; }
kaitai::kstream* _io__raw_f6() const { return m__io__raw_f6.get(); }
std::string _raw_f7() const { return m__raw_f7; }
kaitai::kstream* _io__raw_f7() const { return m__io__raw_f7.get(); }
std::string _raw_f9() const { return m__raw_f9; }
kaitai::kstream* _io__raw_f9() const { return m__io__raw_f9.get(); }
std::string _raw_f10() const { return m__raw_f10; }
kaitai::kstream* _io__raw_f10() const { return m__io__raw_f10.get(); }
};
/**
* \sa https://www.nesdev.org/wiki/PC10_ROM-Images Source
*/
class playchoice10_t : public kaitai::kstruct {
public:
class prom_t;
playchoice10_t(kaitai::kstream* p__io, ines_t* p__parent = nullptr, ines_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~playchoice10_t();
class prom_t : public kaitai::kstruct {
public:
prom_t(kaitai::kstream* p__io, ines_t::playchoice10_t* p__parent = nullptr, ines_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~prom_t();
private:
std::string m_data;
std::string m_counter_out;
ines_t* m__root;
ines_t::playchoice10_t* m__parent;
public:
std::string data() const { return m_data; }
std::string counter_out() const { return m_counter_out; }
ines_t* _root() const { return m__root; }
ines_t::playchoice10_t* _parent() const { return m__parent; }
};
private:
std::string m_inst_rom;
std::unique_ptr<prom_t> m_prom;
ines_t* m__root;
ines_t* m__parent;
public:
std::string inst_rom() const { return m_inst_rom; }
prom_t* prom() const { return m_prom.get(); }
ines_t* _root() const { return m__root; }
ines_t* _parent() const { return m__parent; }
};
private:
std::unique_ptr<header_t> m_header;
std::string m_trainer;
bool n_trainer;
public:
bool _is_null_trainer() { trainer(); return n_trainer; };
private:
std::string m_prg_rom;
std::string m_chr_rom;
std::unique_ptr<playchoice10_t> m_playchoice10;
bool n_playchoice10;
public:
bool _is_null_playchoice10() { playchoice10(); return n_playchoice10; };
private:
std::string m_title;
bool n_title;
public:
bool _is_null_title() { title(); return n_title; };
private:
ines_t* m__root;
kaitai::kstruct* m__parent;
std::string m__raw_header;
std::unique_ptr<kaitai::kstream> m__io__raw_header;
public:
header_t* header() const { return m_header.get(); }
std::string trainer() const { return m_trainer; }
std::string prg_rom() const { return m_prg_rom; }
std::string chr_rom() const { return m_chr_rom; }
playchoice10_t* playchoice10() const { return m_playchoice10.get(); }
std::string title() const { return m_title; }
ines_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
std::string _raw_header() const { return m__raw_header; }
kaitai::kstream* _io__raw_header() const { return m__io__raw_header.get(); }
};
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "ines.h"
#include "kaitai/exceptions.h"
ines_t::ines_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, ines_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this;
m_header = nullptr;
m__io__raw_header = nullptr;
m_playchoice10 = nullptr;
_read();
}
void ines_t::_read() {
m__raw_header = m__io->read_bytes(16);
m__io__raw_header = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_header));
m_header = std::unique_ptr<header_t>(new header_t(m__io__raw_header.get(), this, m__root));
n_trainer = true;
if (header()->f6()->trainer()) {
n_trainer = false;
m_trainer = m__io->read_bytes(512);
}
m_prg_rom = m__io->read_bytes((header()->len_prg_rom() * 16384));
m_chr_rom = m__io->read_bytes((header()->len_chr_rom() * 8192));
n_playchoice10 = true;
if (header()->f7()->playchoice10()) {
n_playchoice10 = false;
m_playchoice10 = std::unique_ptr<playchoice10_t>(new playchoice10_t(m__io, this, m__root));
}
n_title = true;
if (!(_io()->is_eof())) {
n_title = false;
m_title = kaitai::kstream::bytes_to_str(m__io->read_bytes_full(), std::string("ASCII"));
}
}
ines_t::~ines_t() {
_clean_up();
}
void ines_t::_clean_up() {
if (!n_trainer) {
}
if (!n_playchoice10) {
}
if (!n_title) {
}
}
ines_t::header_t::header_t(kaitai::kstream* p__io, ines_t* p__parent, ines_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_f6 = nullptr;
m__io__raw_f6 = nullptr;
m_f7 = nullptr;
m__io__raw_f7 = nullptr;
m_f9 = nullptr;
m__io__raw_f9 = nullptr;
m_f10 = nullptr;
m__io__raw_f10 = nullptr;
f_mapper = false;
_read();
}
void ines_t::header_t::_read() {
m_magic = m__io->read_bytes(4);
if (!(magic() == std::string("\x4E\x45\x53\x1A", 4))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x4E\x45\x53\x1A", 4), magic(), _io(), std::string("/types/header/seq/0"));
}
m_len_prg_rom = m__io->read_u1();
m_len_chr_rom = m__io->read_u1();
m__raw_f6 = m__io->read_bytes(1);
m__io__raw_f6 = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_f6));
m_f6 = std::unique_ptr<f6_t>(new f6_t(m__io__raw_f6.get(), this, m__root));
m__raw_f7 = m__io->read_bytes(1);
m__io__raw_f7 = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_f7));
m_f7 = std::unique_ptr<f7_t>(new f7_t(m__io__raw_f7.get(), this, m__root));
m_len_prg_ram = m__io->read_u1();
m__raw_f9 = m__io->read_bytes(1);
m__io__raw_f9 = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_f9));
m_f9 = std::unique_ptr<f9_t>(new f9_t(m__io__raw_f9.get(), this, m__root));
m__raw_f10 = m__io->read_bytes(1);
m__io__raw_f10 = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_f10));
m_f10 = std::unique_ptr<f10_t>(new f10_t(m__io__raw_f10.get(), this, m__root));
m_reserved = m__io->read_bytes(5);
if (!(reserved() == std::string("\x00\x00\x00\x00\x00", 5))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x00\x00\x00\x00\x00", 5), reserved(), _io(), std::string("/types/header/seq/8"));
}
}
ines_t::header_t::~header_t() {
_clean_up();
}
void ines_t::header_t::_clean_up() {
}
ines_t::header_t::f6_t::f6_t(kaitai::kstream* p__io, ines_t::header_t* p__parent, ines_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
_read();
}
void ines_t::header_t::f6_t::_read() {
m_lower_mapper = m__io->read_bits_int_be(4);
m_four_screen = m__io->read_bits_int_be(1);
m_trainer = m__io->read_bits_int_be(1);
m_has_battery_ram = m__io->read_bits_int_be(1);
m_mirroring = static_cast<ines_t::header_t::f6_t::mirroring_t>(m__io->read_bits_int_be(1));
}
ines_t::header_t::f6_t::~f6_t() {
_clean_up();
}
void ines_t::header_t::f6_t::_clean_up() {
}
ines_t::header_t::f7_t::f7_t(kaitai::kstream* p__io, ines_t::header_t* p__parent, ines_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
_read();
}
void ines_t::header_t::f7_t::_read() {
m_upper_mapper = m__io->read_bits_int_be(4);
m_format = m__io->read_bits_int_be(2);
m_playchoice10 = m__io->read_bits_int_be(1);
m_vs_unisystem = m__io->read_bits_int_be(1);
}
ines_t::header_t::f7_t::~f7_t() {
_clean_up();
}
void ines_t::header_t::f7_t::_clean_up() {
}
ines_t::header_t::f9_t::f9_t(kaitai::kstream* p__io, ines_t::header_t* p__parent, ines_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
_read();
}
void ines_t::header_t::f9_t::_read() {
m_reserved = m__io->read_bits_int_be(7);
m_tv_system = static_cast<ines_t::header_t::f9_t::tv_system_t>(m__io->read_bits_int_be(1));
}
ines_t::header_t::f9_t::~f9_t() {
_clean_up();
}
void ines_t::header_t::f9_t::_clean_up() {
}
ines_t::header_t::f10_t::f10_t(kaitai::kstream* p__io, ines_t::header_t* p__parent, ines_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
_read();
}
void ines_t::header_t::f10_t::_read() {
m_reserved1 = m__io->read_bits_int_be(2);
m_bus_conflict = m__io->read_bits_int_be(1);
m_prg_ram = m__io->read_bits_int_be(1);
m_reserved2 = m__io->read_bits_int_be(2);
m_tv_system = static_cast<ines_t::header_t::f10_t::tv_system_t>(m__io->read_bits_int_be(2));
}
ines_t::header_t::f10_t::~f10_t() {
_clean_up();
}
void ines_t::header_t::f10_t::_clean_up() {
}
int32_t ines_t::header_t::mapper() {
if (f_mapper)
return m_mapper;
m_mapper = (f6()->lower_mapper() | (f7()->upper_mapper() << 4));
f_mapper = true;
return m_mapper;
}
ines_t::playchoice10_t::playchoice10_t(kaitai::kstream* p__io, ines_t* p__parent, ines_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_prom = nullptr;
_read();
}
void ines_t::playchoice10_t::_read() {
m_inst_rom = m__io->read_bytes(8192);
m_prom = std::unique_ptr<prom_t>(new prom_t(m__io, this, m__root));
}
ines_t::playchoice10_t::~playchoice10_t() {
_clean_up();
}
void ines_t::playchoice10_t::_clean_up() {
}
ines_t::playchoice10_t::prom_t::prom_t(kaitai::kstream* p__io, ines_t::playchoice10_t* p__parent, ines_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
_read();
}
void ines_t::playchoice10_t::prom_t::_read() {
m_data = m__io->read_bytes(16);
m_counter_out = m__io->read_bytes(16);
}
ines_t::playchoice10_t::prom_t::~prom_t() {
_clean_up();
}
void ines_t::playchoice10_t::prom_t::_clean_up() {
}