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/main.cpp | 437 ------------------------------------------- 1 file changed, 437 deletions(-) delete mode 100644 tools/sprite_dumper/main.cpp (limited to 'tools/sprite_dumper/main.cpp') 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