From a64c85a3f5641581a19ef52a5c7898ec3cb71754 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sun, 31 Jan 2021 20:04:51 -0500 Subject: Abstracted some of the sprite dumper functionality out --- tools/sprite_dumper/CMakeLists.txt | 8 +- tools/sprite_dumper/common.h | 139 +++++++++++ tools/sprite_dumper/main.cpp | 437 --------------------------------- tools/sprite_dumper/sprite_dumper.cpp | 361 +++++++++++++++++++++++++++ tools/sprite_dumper/tileset_dumper.cpp | 39 +++ 5 files changed, 545 insertions(+), 439 deletions(-) create mode 100644 tools/sprite_dumper/common.h delete mode 100644 tools/sprite_dumper/main.cpp create mode 100644 tools/sprite_dumper/sprite_dumper.cpp create mode 100644 tools/sprite_dumper/tileset_dumper.cpp diff --git a/tools/sprite_dumper/CMakeLists.txt b/tools/sprite_dumper/CMakeLists.txt index 5a90c5e..62f4a28 100644 --- a/tools/sprite_dumper/CMakeLists.txt +++ b/tools/sprite_dumper/CMakeLists.txt @@ -8,8 +8,12 @@ pkg_check_modules(GraphicsMagick GraphicsMagick++ REQUIRED) include_directories(${GraphicsMagick_INCLUDE_DIRS}) -add_executable(sprite_dumper main.cpp) - +add_executable(sprite_dumper sprite_dumper.cpp) set_property(TARGET sprite_dumper PROPERTY CXX_STANDARD 17) set_property(TARGET sprite_dumper PROPERTY CXX_STANDARD_REQUIRED ON) target_link_libraries(sprite_dumper ${GraphicsMagick_LIBRARIES}) + +add_executable(tileset_dumper tileset_dumper.cpp) +set_property(TARGET tileset_dumper PROPERTY CXX_STANDARD 17) +set_property(TARGET tileset_dumper PROPERTY CXX_STANDARD_REQUIRED ON) +target_link_libraries(tileset_dumper ${GraphicsMagick_LIBRARIES}) diff --git a/tools/sprite_dumper/common.h b/tools/sprite_dumper/common.h new file mode 100644 index 0000000..dca5de9 --- /dev/null +++ b/tools/sprite_dumper/common.h @@ -0,0 +1,139 @@ +#ifndef COMMON_H_04DD2B2A +#define COMMON_H_04DD2B2A + +#include +#include +#include +#include + +class BufferView { +public: + + BufferView(const std::vector& buffer) : data_(&buffer) {} + + int ReadNextByte() { + int o2r = offset_; + offset_++; + return ReadByte(o2r); + } + + int ReadByte(int addr) const { + return static_cast((*data_)[addr]); + } + + int ReadNextTwoBytes() { + int o2r = offset_; + offset_+=2; + return ReadTwoBytes(o2r); + } + + int ReadTwoBytes(int addr) const { + return static_cast((*data_)[addr]) | (static_cast((*data_)[addr + 1]) << 8); + } + + int ReadNextFourBytes() { + int o2r = offset_; + offset_+=4; + return ReadFourBytes(o2r); + } + + unsigned long ReadFourBytes(int addr) const { + return static_cast((*data_)[addr]) | (static_cast((*data_)[addr + 1]) << 8) | (static_cast((*data_)[addr + 2]) << 16) | (static_cast((*data_)[addr + 3]) << 24); + } + + void Seek(int offset) { + offset_ = offset; + } + + void SeekAdd(int delta) { + offset_ += delta; + } + + std::vector Decompress(int addr) { + int start = addr; + Seek(addr); + if (ReadNextByte() != 0x10) { + throw std::domain_error("No LZ77 signature"); + } + + int length = ReadNextByte(); + length += (ReadNextByte() << 8); + length += (ReadNextByte() << 16); + + std::vector result(length, 0); + + int bPos = 0; + while (bPos < length) { + unsigned char ch = ReadNextByte(); + for (int i=0;i<8;i++) { + switch ((ch >> (7-i)) & 1) { + case 0: { + if (bPos >= length) break; + result[bPos++] = ReadNextByte(); + break; + } + case 1: { + int t = (ReadNextByte() << 8); + t += ReadNextByte(); + int n = ((t >> 12) & 0xF) + 3; // num of bytes to copy + int o = (t & 0xFFF); + + memcpy(result.data() + bPos, result.data() + bPos - o - 1, n); + bPos += n; + break; + } + default: break; + } + } + } + + return result; + } + +private: + + const std::vector* data_; + int offset_ = 0; +}; + +class Rom { +public: + + explicit Rom(std::string_view filename) { + std::ifstream romfile(filename.data(), std::ios::binary); + if (!romfile.is_open()) { + throw std::invalid_argument("Could not find ROM file: " + std::string(filename)); + } + + romfile.seekg(0, romfile.end); + int length = romfile.tellg(); + romfile.seekg(0, romfile.beg); + + if (length != 0x2000000) { + throw std::invalid_argument("Incorrect ROM length"); + } + + data_ = std::vector(length, 0); + romfile.read(data_.data(), length); + + const char header[] = {'M','O','T','H','E','R','3',0,0,0,0,0,'A','3','U','J'}; + std::string headerTest; + for (int i = 0xA0; i < 0xB0; i++) { + if (data_.at(i) != header[i-0xA0]) { + std::cout << i << std::endl; + + throw std::invalid_argument("Invalid ROM header"); + } + } + } + + const std::vector& data() const { return data_; } + + BufferView buffer() const { return {data_}; } + +private: + + std::vector data_; +}; + +#endif /* end of include guard: COMMON_H_04DD2B2A */ diff --git a/tools/sprite_dumper/main.cpp b/tools/sprite_dumper/main.cpp deleted file mode 100644 index 2ba37c8..0000000 --- a/tools/sprite_dumper/main.cpp +++ /dev/null @@ -1,437 +0,0 @@ -// Inspired a lot by https://github.com/jeffman/MOTHER-3-Funland - -#include -#include -#include -#include -#include -#include -#include -#include - -class Rom { -public: - - explicit Rom(std::string_view filename) { - std::ifstream romfile(filename.data(), std::ios::binary); - if (!romfile.is_open()) { - throw std::invalid_argument("Could not find ROM file: " + std::string(filename)); - } - - romfile.seekg(0, romfile.end); - int length = romfile.tellg(); - romfile.seekg(0, romfile.beg); - - if (length != 0x2000000) { - throw std::invalid_argument("Incorrect ROM length"); - } - - data_ = std::vector(length, 0); - romfile.read(data_.data(), length); - - const char header[] = {'M','O','T','H','E','R','3',0,0,0,0,0,'A','3','U','J'}; - std::string headerTest; - for (int i = 0xA0; i < 0xB0; i++) { - if (data_.at(i) != header[i-0xA0]) { - std::cout << i << std::endl; - - throw std::invalid_argument("Invalid ROM header"); - } - } - } - - const std::vector& data() const { return data_; } - - int ReadNextByte() { - int o2r = offset_; - offset_++; - return ReadByte(o2r); - } - - int ReadByte(int addr) const { - return static_cast(data_[addr]); - } - - int ReadNextTwoBytes() { - int o2r = offset_; - offset_+=2; - return ReadTwoBytes(o2r); - } - - int ReadTwoBytes(int addr) const { - return static_cast(data_[addr]) | (static_cast(data_[addr + 1]) << 8); - } - - int ReadNextFourBytes() { - int o2r = offset_; - offset_+=4; - return ReadFourBytes(o2r); - } - - unsigned long ReadFourBytes(int addr) const { - return static_cast(data_[addr]) | (static_cast(data_[addr + 1]) << 8) | (static_cast(data_[addr + 2]) << 16) | (static_cast(data_[addr + 3]) << 24); - } - - void Seek(int offset) { - offset_ = offset; - } - - void SeekAdd(int delta) { - offset_ += delta; - } - -private: - - std::vector data_; - int offset_ = 0; -}; - -class Palette { -public: - - Palette() = default; - - Palette(Rom& m3, const int addr) { - for (int i=0; i<16; i++) { - unsigned short ch = m3.ReadTwoBytes(addr + (i << 1)); - int r = (ch & 0x1F); - int g = ((ch >> 5) & 0x1F); - int b = ((ch >> 10) & 0x1F); - colors_.push_back(Magick::ColorRGB((r << 3)/256.0, (g << 3)/256.0, (b << 3)/256.0)); - } - } - - const std::vector& Colors() const { return colors_; } - -private: - std::vector colors_; -}; - -class PaletteSet { -public: - PaletteSet(Rom& m3) : m3_(m3) { - for (int i=0; i<16; i++) { - defaultPals_.emplace_back(m3, GetPointer(0) + (i << 5)); - } - } - - const Palette& GetPalette(int spriteindex, int palnum = -1) { - int a = GetPointer(spriteindex); - - if ((spriteindex > 0xFF) && (spriteindex < 0x26C)) { - specialPals_[spriteindex] = Palette(m3_, a); - return specialPals_[spriteindex]; - } else { - if (palnum == -1) { - palnum = static_cast(m3_.ReadByte(0x1433D7C + 1 + ((spriteindex + 1) * 12))) & 0xF; - } - - return defaultPals_.at(palnum); - } - } - -private: - - int GetPointer(int spriteindex) { - const int ADDR = 0x1A41548; - - int index = 0; - - if ((spriteindex > 0xFF) && (spriteindex < 0x26C)) { - // These sprites use the (spriteindex - 0xFF)th entry - index = spriteindex - 0xff; - } - - int a = m3_.ReadFourBytes(ADDR + 4 + (index << 2)); - if (a == -1) return -1; - return ADDR + a; - } - - Rom& m3_; - std::vector defaultPals_; - std::map specialPals_; -}; - -struct Subsprite { - int x; - int y; - int tile; - bool flipH; - bool flipV; - int objSize; - int objShape; - - int Width() const { return WIDTHS[objShape][objSize]; } - - int Height() const { return HEIGHTS[objShape][objSize]; } - -private: - - static constexpr int WIDTHS[3][4] = { { 8, 16, 32, 64 }, { 16, 32, 32, 64 }, { 8, 8, 16, 32 } }; - static constexpr int HEIGHTS[3][4] = { { 8, 16, 32, 64 }, { 8, 8, 16, 32 }, { 16, 32, 32, 64 } }; -}; - -struct FrameOutput { - Magick::Image image; - int width; - int height; - int centerX; - int centerY; -}; - -struct Sprite { - std::vector subsprites; - - FrameOutput render(Rom& m3, const Palette& palette, const int gfxPtr) const { - FrameOutput output; - - if (subsprites.empty()) return output; - - int minX = subsprites[0].x; - int minY = subsprites[0].y; - int maxX = minX + subsprites[0].Width(); - int maxY = minY + subsprites[0].Height(); - - for (int j=1; j maxX) maxX = o.x + o.Width(); - if ((o.y + o.Height()) > maxY) maxY = o.y + o.Height(); - } - - int width = maxX - minX; - int height = maxY - minY; - int centerX = -minX; - int centerY = -minY; - - output.width = width; - output.height = height; - output.centerX = centerX; - output.centerY = centerY; - output.image = Magick::Image(Magick::Geometry(width, height), "transparent"); - output.image.modifyImage(); - - Magick::Pixels view(output.image); - - for (const Subsprite& o : subsprites) { - int tilePointer = o.tile << 5; - for (int y=0; y(ch & 0xF); - tileData[tx*2+1][ty] = static_cast((ch >> 4) & 0xF); - } - } - - tilePointer += 0x20; - - int actualX = o.flipH ? (o.Width() - x - 8) : x; - - int destX = o.x + centerX + actualX; - int destY = o.y + centerY + actualY; - - Magick::PixelPacket* pixels = view.get(destX,destY,8,8); - - for (int ty=0; ty<8; ty++) { - int actualTy = o.flipV ? (7-ty) : ty; - - for (int tx=0; tx<8; tx++) { - int actualTx = o.flipH ? (7-tx) : tx; - - if (tileData[actualTx][actualTy] != 0) { - //auto& c = palette.Colors().at(tileData[actualTx][actualTy]); - //std::cout << c.redQuantum() << "," << c.greenQuantum() << "," << c.blueQuantum() << std::endl; - *pixels = palette.Colors().at(tileData[actualTx][actualTy]); - //std::cout << tileData[actualTx][actualTy] << std::endl; - } - pixels++; - } - } - - view.sync(); - } - } - } - - return output; - } -}; - -struct SpriteSheet { - int spriteindex = 0; - int gfxPtr; - std::vector sprites; - - void render(Rom& m3, PaletteSet& palettes, std::vector& frames) const { - const Palette& palette = palettes.GetPalette(spriteindex); - for (int i=0; i(m3.ReadNextByte()); - subsprite.x = static_cast(m3.ReadNextByte()); - - unsigned short ch = m3.ReadNextTwoBytes(); - subsprite.tile = static_cast(ch & 0x3FF); - subsprite.flipH = (ch & 0x400) != 0; - subsprite.flipV = (ch & 0x800) != 0; - subsprite.objSize = static_cast((ch >> 12) & 3); - subsprite.objShape = static_cast((ch >> 14) & 3); - - /*std::cout << subsprite.y << std::endl; - std::cout << subsprite.x << std::endl; - std::cout << subsprite.flipH << "," << subsprite.flipV << std::endl;*/ - - sprite.subsprites.push_back(std::move(subsprite)); - } - - m3.SeekAdd(2); - - ss.sprites.push_back(std::move(sprite)); - } - - spritesheets_[i] = std::move(ss); - //if (i == 15) return; - } - } - - const std::map& SpriteSheets() const { return spritesheets_; } - -private: - - int GetPointerToSheet(Rom& m3, int index) { - int readAt = baseAddr_ + 4 + (index << 2); - int a = m3.ReadFourBytes(readAt); - //std::cout << readAt << " :: " << a << std::hex << std::endl; - if (a == 0) return -1; - return a + baseAddr_; - } - - int GetGfxPointer(Rom& m3, int index) { - return m3.ReadFourBytes(gfxAddr_ + 4 + (index << 2)) + gfxAddr_; - } - - int baseAddr_; - int gfxAddr_; - std::map spritesheets_; -}; - -int main(int argc, char** argv) { - if (argc < 3) { - std::cout << "Usage: ./sprite_dumper [path to rom] {sheet IDs}" << std::endl; - std::cout << "sheet IDs should be space separated references to the sheets to concatenate" << std::endl; - std::cout << "the format of the ID is BankNum.SheetNum" << std::endl; - return -1; - } - - Magick::InitializeMagick(nullptr); - - Rom m3(argv[1]); - PaletteSet palettes(m3); - Bank b1(m3, 0x1A442A4, 0x14383E4); - Bank b2(m3, 0x1AE0638, 0x194BC30); - Bank b3(m3, 0x1AEE4C4, 0x1A012B8); - Bank b4(m3, 0x1AF1ED0, 0x1A36AA0); - - std::vector frames; - - for (int i=2; i> bankNum; - argfmt >> ch; //. - argfmt >> sheetNum; - - const Bank& bankToRead = [&](){ - switch (bankNum) { - case 0: return b1; - case 1: return b2; - case 2: return b3; - case 3: return b4; - default: throw std::invalid_argument("Invalid bank num: " + std::to_string(bankNum)); - } - }(); - - bankToRead.SpriteSheets().at(sheetNum).render(m3, palettes, frames); - } - - int maxWidth = 0; - int maxHeight = 0; - for (const FrameOutput& f : frames) { - if (f.width > maxWidth) maxWidth = f.width; - if (f.height > maxHeight) maxHeight = f.height; - } - - const int FRAMES_PER_ROW = 10; - int sheetWidth; - int sheetHeight; - - std::ofstream datafile("out.txt"); - datafile << maxWidth << "," << maxHeight << " cell size" << std::endl; - datafile << FRAMES_PER_ROW << " frames per row" << std::endl; - datafile << frames.size() << " frames" << std::endl; - datafile << std::endl; - - if (frames.size() < FRAMES_PER_ROW) { - sheetWidth = frames.size() * maxWidth; - sheetHeight = maxHeight; - } else { - sheetWidth = FRAMES_PER_ROW * maxWidth; - sheetHeight = (frames.size() / FRAMES_PER_ROW + 1) * maxHeight; - } - - Magick::Image sheet(Magick::Geometry(sheetWidth, sheetHeight), "transparent"); - for (int i=0; i +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +class Palette { +public: + + Palette() = default; + + Palette(BufferView m3, const int addr) { + for (int i=0; i<16; i++) { + unsigned short ch = m3.ReadTwoBytes(addr + (i << 1)); + int r = (ch & 0x1F); + int g = ((ch >> 5) & 0x1F); + int b = ((ch >> 10) & 0x1F); + colors_.push_back(Magick::ColorRGB((r << 3)/256.0, (g << 3)/256.0, (b << 3)/256.0)); + } + } + + const std::vector& Colors() const { return colors_; } + +private: + std::vector colors_; +}; + +class PaletteSet { +public: + PaletteSet(BufferView m3) : m3_(m3) { + for (int i=0; i<16; i++) { + defaultPals_.emplace_back(m3, GetPointer(0) + (i << 5)); + } + } + + const Palette& GetPalette(int spriteindex, int palnum = -1) { + int a = GetPointer(spriteindex); + + if ((spriteindex > 0xFF) && (spriteindex < 0x26C)) { + specialPals_[spriteindex] = Palette(m3_, a); + return specialPals_[spriteindex]; + } else { + if (palnum == -1) { + palnum = static_cast(m3_.ReadByte(0x1433D7C + 1 + ((spriteindex + 1) * 12))) & 0xF; + } + + return defaultPals_.at(palnum); + } + } + +private: + + int GetPointer(int spriteindex) { + const int ADDR = 0x1A41548; + + int index = 0; + + if ((spriteindex > 0xFF) && (spriteindex < 0x26C)) { + // These sprites use the (spriteindex - 0xFF)th entry + index = spriteindex - 0xff; + } + + int a = m3_.ReadFourBytes(ADDR + 4 + (index << 2)); + if (a == -1) return -1; + return ADDR + a; + } + + BufferView m3_; + std::vector defaultPals_; + std::map specialPals_; +}; + +struct Subsprite { + int x; + int y; + int tile; + bool flipH; + bool flipV; + int objSize; + int objShape; + + int Width() const { return WIDTHS[objShape][objSize]; } + + int Height() const { return HEIGHTS[objShape][objSize]; } + +private: + + static constexpr int WIDTHS[3][4] = { { 8, 16, 32, 64 }, { 16, 32, 32, 64 }, { 8, 8, 16, 32 } }; + static constexpr int HEIGHTS[3][4] = { { 8, 16, 32, 64 }, { 8, 8, 16, 32 }, { 16, 32, 32, 64 } }; +}; + +struct FrameOutput { + Magick::Image image; + int width; + int height; + int centerX; + int centerY; +}; + +struct Sprite { + std::vector subsprites; + + FrameOutput render(BufferView m3, const Palette& palette, const int gfxPtr) const { + FrameOutput output; + + if (subsprites.empty()) return output; + + int minX = subsprites[0].x; + int minY = subsprites[0].y; + int maxX = minX + subsprites[0].Width(); + int maxY = minY + subsprites[0].Height(); + + for (int j=1; j maxX) maxX = o.x + o.Width(); + if ((o.y + o.Height()) > maxY) maxY = o.y + o.Height(); + } + + int width = maxX - minX; + int height = maxY - minY; + int centerX = -minX; + int centerY = -minY; + + output.width = width; + output.height = height; + output.centerX = centerX; + output.centerY = centerY; + output.image = Magick::Image(Magick::Geometry(width, height), "transparent"); + output.image.modifyImage(); + + Magick::Pixels view(output.image); + + for (const Subsprite& o : subsprites) { + int tilePointer = o.tile << 5; + for (int y=0; y(ch & 0xF); + tileData[tx*2+1][ty] = static_cast((ch >> 4) & 0xF); + } + } + + tilePointer += 0x20; + + int actualX = o.flipH ? (o.Width() - x - 8) : x; + + int destX = o.x + centerX + actualX; + int destY = o.y + centerY + actualY; + + Magick::PixelPacket* pixels = view.get(destX,destY,8,8); + + for (int ty=0; ty<8; ty++) { + int actualTy = o.flipV ? (7-ty) : ty; + + for (int tx=0; tx<8; tx++) { + int actualTx = o.flipH ? (7-tx) : tx; + + if (tileData[actualTx][actualTy] != 0) { + //auto& c = palette.Colors().at(tileData[actualTx][actualTy]); + //std::cout << c.redQuantum() << "," << c.greenQuantum() << "," << c.blueQuantum() << std::endl; + *pixels = palette.Colors().at(tileData[actualTx][actualTy]); + //std::cout << tileData[actualTx][actualTy] << std::endl; + } + pixels++; + } + } + + view.sync(); + } + } + } + + return output; + } +}; + +struct SpriteSheet { + int spriteindex = 0; + int gfxPtr; + std::vector sprites; + + void render(BufferView m3, PaletteSet& palettes, std::vector& frames) const { + const Palette& palette = palettes.GetPalette(spriteindex); + for (int i=0; i(m3.ReadNextByte()); + subsprite.x = static_cast(m3.ReadNextByte()); + + unsigned short ch = m3.ReadNextTwoBytes(); + subsprite.tile = static_cast(ch & 0x3FF); + subsprite.flipH = (ch & 0x400) != 0; + subsprite.flipV = (ch & 0x800) != 0; + subsprite.objSize = static_cast((ch >> 12) & 3); + subsprite.objShape = static_cast((ch >> 14) & 3); + + /*std::cout << subsprite.y << std::endl; + std::cout << subsprite.x << std::endl; + std::cout << subsprite.flipH << "," << subsprite.flipV << std::endl;*/ + + sprite.subsprites.push_back(std::move(subsprite)); + } + + m3.SeekAdd(2); + + ss.sprites.push_back(std::move(sprite)); + } + + spritesheets_[i] = std::move(ss); + //if (i == 15) return; + } + } + + const std::map& SpriteSheets() const { return spritesheets_; } + +private: + + int GetPointerToSheet(BufferView m3, int index) { + int readAt = baseAddr_ + 4 + (index << 2); + int a = m3.ReadFourBytes(readAt); + //std::cout << readAt << " :: " << a << std::hex << std::endl; + if (a == 0) return -1; + return a + baseAddr_; + } + + int GetGfxPointer(BufferView m3, int index) { + return m3.ReadFourBytes(gfxAddr_ + 4 + (index << 2)) + gfxAddr_; + } + + int baseAddr_; + int gfxAddr_; + std::map spritesheets_; +}; + +int main(int argc, char** argv) { + if (argc < 3) { + std::cout << "Usage: ./sprite_dumper [path to rom] {sheet IDs}" << std::endl; + std::cout << "sheet IDs should be space separated references to the sheets to concatenate" << std::endl; + std::cout << "the format of the ID is BankNum.SheetNum" << std::endl; + return -1; + } + + Magick::InitializeMagick(nullptr); + + Rom m3(argv[1]); + PaletteSet palettes(m3.buffer()); + Bank b1(m3.buffer(), 0x1A442A4, 0x14383E4); + Bank b2(m3.buffer(), 0x1AE0638, 0x194BC30); + Bank b3(m3.buffer(), 0x1AEE4C4, 0x1A012B8); + Bank b4(m3.buffer(), 0x1AF1ED0, 0x1A36AA0); + + std::vector frames; + + for (int i=2; i> bankNum; + argfmt >> ch; //. + argfmt >> sheetNum; + + const Bank& bankToRead = [&](){ + switch (bankNum) { + case 0: return b1; + case 1: return b2; + case 2: return b3; + case 3: return b4; + default: throw std::invalid_argument("Invalid bank num: " + std::to_string(bankNum)); + } + }(); + + bankToRead.SpriteSheets().at(sheetNum).render(m3.buffer(), palettes, frames); + } + + int maxWidth = 0; + int maxHeight = 0; + for (const FrameOutput& f : frames) { + if (f.width > maxWidth) maxWidth = f.width; + if (f.height > maxHeight) maxHeight = f.height; + } + + const int FRAMES_PER_ROW = 10; + int sheetWidth; + int sheetHeight; + + std::ofstream datafile("out.txt"); + datafile << maxWidth << "," << maxHeight << " cell size" << std::endl; + datafile << FRAMES_PER_ROW << " frames per row" << std::endl; + datafile << frames.size() << " frames" << std::endl; + datafile << std::endl; + + if (frames.size() < FRAMES_PER_ROW) { + sheetWidth = frames.size() * maxWidth; + sheetHeight = maxHeight; + } else { + sheetWidth = FRAMES_PER_ROW * maxWidth; + sheetHeight = (frames.size() / FRAMES_PER_ROW + 1) * maxHeight; + } + + Magick::Image sheet(Magick::Geometry(sheetWidth, sheetHeight), "transparent"); + for (int i=0; i +#include +#include +#include +#include "common.h" + +int main(int argc, char** argv) { + if (argc < 3) { + std::cout << "Usage: ./tileset_dumper [path to rom] {map ID}" << std::endl; + return -1; + } + + Magick::InitializeMagick(nullptr); + + Rom m3(argv[1]); + + int roomNum = std::stoi(argv[2]); + const unsigned long ROOM_TILES_BASE = 0x104D9CC; + unsigned long metatilesInfoAddr = ROOM_TILES_BASE + 4 + (roomNum << 2); + unsigned long metatilesOffset = m3.buffer().ReadFourBytes(metatilesInfoAddr); + unsigned long metatilesAddr = metatilesOffset + ROOM_TILES_BASE; + std::vector metatiles = m3.buffer().Decompress(metatilesAddr); + std::cout << (metatiles.size()/8) << std::endl; + + for (int i=0; i<10; i++) { + int tile00 = metatiles[i*8+4]; + int tile01 = metatiles[i*8+5]; + int tile10 = metatiles[i*8+6]; + int tile11 = metatiles[i*8+7]; + + int mask = metatiles[i*8+2]; + if ((mask & 0x1) == 0) tile00 = -1; + if ((mask & 0x2) == 0) tile01 = -1; + if ((mask & 0x4) == 0) tile10 = -1; + if ((mask & 0x8) == 0) tile11 = -1; + + std::cout << tile00 << "," << tile01 << "," << tile10 << "," << tile11 << std::endl; + } +} \ No newline at end of file -- cgit 1.4.1