about summary refs log tree commit diff stats
path: root/data/maps/the_words/connections.txtpb
blob: f25ac2b9266329c0b6c8c7eeb739f43a75743276 (plain) (blame)
1
2
3
4
5
connections {
  from_room: "Main Area"
  to_room: "Ending"
  door { name: "Ending" }
}
n209' href='#n209'>209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
#include <iostream>
#include <Magick++.h>
#include <vector>
#include <string>
#include <map>
#include "common.h"

constexpr int NUM_ROOMS = 1000;
constexpr int NUM_TILESETS = 12;

struct RoomInfo {
  int width;
  int height;

  static std::map<int, RoomInfo> ReadFromRom(BufferView m3) {
    const int BASE_ADDR = 0xD2E1D8 + 12;
    const int ENTRY_LEN = 28;

    std::map<int, RoomInfo> output;

    for (int i=0; i<NUM_ROOMS; i++) {
      RoomInfo& ri = output[i];

      ri.width = ((m3.ReadByte(BASE_ADDR + i*ENTRY_LEN + 20) & 7) + 1) << 4;
      ri.height = (((m3.ReadByte(BASE_ADDR + i*ENTRY_LEN + 24) & 0x3F) >> 3) + 1) << 4;
    }

    return output;
  }
};

struct RoomGfxPal {
  int paletteId;
  int tilesetId[NUM_TILESETS];

  static std::map<int, RoomGfxPal> ReadFromRom(BufferView m3) {
    const int BASE_ADDR = 0xD34F44 + 12;
    const int ENTRY_LEN = 26;

    std::map<int, RoomGfxPal> output;

    for (int i=0; i<NUM_ROOMS; i++) {
      RoomGfxPal& rgp = output[i];

      rgp.paletteId = static_cast<short>(m3.ReadTwoBytes(BASE_ADDR + i*ENTRY_LEN + 24));
      for (int tid=0; tid<NUM_TILESETS; tid++) {
        rgp.tilesetId[tid] = m3.ReadTwoBytes(BASE_ADDR + i*ENTRY_LEN + tid*2);
      }
    }

    return output;
  }

  std::vector<std::vector<char>> GetTilesets(BufferView m3) const {
    const int BASE_ADDR = 0xD3B4E0;

    std::vector<std::vector<char>> output(NUM_TILESETS);

    for (int i=0; i<NUM_TILESETS; i++) {
      if (tilesetId[i] < (NUM_ROOMS * 3)) {
        unsigned long gfxAddr = m3.ReadFourBytes(BASE_ADDR + 4 + (tilesetId[i] << 2)) + BASE_ADDR;
        output[i] = m3.Decompress(gfxAddr);
      }
    }

    return output;
  }

  std::vector<Palette> GetPalettes(BufferView m3) const {
    const int BASE_ADDR = 0xF3C344;

    std::vector<Palette> output;
    int offset = m3.ReadFourBytes(BASE_ADDR + 4 + (paletteId << 2));

    for (int i=0; i<16; i++) {
      unsigned long palAddr = BASE_ADDR + offset + (i << 5);
      output.emplace_back(m3, palAddr);
    }

    return output;
  }
};

std::vector<char> GetMapTiles(BufferView m3, int roomId) {
  const int BASE_ADDR = 0x104D9CC;
  int tilesAddr = m3.ReadFourBytes(BASE_ADDR + 4 + (roomId << 2)) + BASE_ADDR;
  return m3.Decompress(tilesAddr);
}

std::vector<std::vector<char>> GetMapLayers(BufferView m3, int roomId) {
  const int BASE_ADDR = 0xF9003C;
  std::vector<std::vector<char>> layers;
  for (int i=0; i<3; i++) {
    try {
      int lookAddr = BASE_ADDR + 4 + (((roomId * 3) + i) << 2);
      int layerAddr = m3.ReadFourBytes(lookAddr) + BASE_ADDR;
      layers.push_back(m3.Decompress(layerAddr));
    } catch (const std::domain_error&) {
      // ignore
    }
  }
  return layers;
}

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]);
  auto roomInfos = RoomInfo::ReadFromRom(m3.buffer());
  auto roomGfxPals = RoomGfxPal::ReadFromRom(m3.buffer());

  int roomNum = std::stoi(argv[2]);

  std::cout << roomInfos[roomNum].width << "," << roomInfos[roomNum].height << std::endl;
  std::cout << roomGfxPals[roomNum].paletteId;
  for (int i=0;i<12;i++) std::cout << "," << roomGfxPals[roomNum].tilesetId[i];
  std::cout << std::endl;

  int width = roomInfos[roomNum].width;
  int height = roomInfos[roomNum].height;

  Magick::Image image(Magick::Geometry(width*16, height*16), "transparent");
  image.modifyImage();
  Magick::Pixels view(image);

  auto mapTiles = GetMapTiles(m3.buffer(), roomNum);
  auto mapLayers = GetMapLayers(m3.buffer(), roomNum);

  const auto& gfxPal = roomGfxPals[roomNum];
  auto palettes = gfxPal.GetPalettes(m3.buffer());
  auto tilesets = gfxPal.GetTilesets(m3.buffer());

  for (int layer=mapLayers.size()-1; layer>=0; layer--) {
    const std::vector<char>& ml = mapLayers[layer];
    if (ml.empty()) continue;

    for (int mapy = 0; mapy < height; mapy++) {
      for (int mapx = 0; mapx < width; mapx++) {
        unsigned short ch = BufferView(ml).ReadTwoBytes((mapx + (mapy * width)) * 2);
        unsigned short tile16 = ch & 0x3FF;

        if ((tile16 >> 6) >= 12) continue;

        int tpal = (ch >> 10) & 0xF;
        const Palette& palette = palettes[tpal];

        bool tflipx = (ch & 0x4000) != 0;
        bool tflipy = (ch & 0x8000) != 0;

        int tile8[2][2];
        bool sflipx[2][2];
        bool sflipy[2][2];

        unsigned int magic = BufferView(mapTiles).ReadFourBytes(tile16 * 8);

        tile8[0][0] = mapTiles[(tile16 * 8) + 4];
        tile8[0][1] = mapTiles[(tile16 * 8) + 5];
        tile8[1][0] = mapTiles[(tile16 * 8) + 6];
        tile8[1][1] = mapTiles[(tile16 * 8) + 7];

        for (int i=0; i<2; i++) {
          for (int j=0; j<2; j++) {
            sflipx[i][j] = (tile8[i][j] & 0x40) != 0;
            sflipy[i][j] = (tile8[i][j] & 0x80) != 0;

            tile8[i][j] &= 0x3f;
            tile8[i][j] |= (ch & 0x3c0);
          }
        }

        unsigned int mask = (magic >> 16) & 0xf;
        if ((mask & 0x1) == 0) tile8[0][0] = -1;
        if ((mask & 0x2) == 0) tile8[0][1] = -1;
        if ((mask & 0x4) == 0) tile8[1][0] = -1;
        if ((mask & 0x8) == 0) tile8[1][1] = -1;

        for (int tiley=0; tiley<2; tiley++) {
          for (int tilex=0; tilex<2; tilex++) {
            if (tile8[tiley][tilex] < 0) continue;

            int tileset = tile8[tiley][tilex] >> 6;
            int subtile = tile8[tiley][tilex] & 0x3f;

            int tileData[8][8];
            BufferView tilesetData(tilesets[tileset]);
            tilesetData.Seek(subtile << 5);
            for (int ty=0; ty<8; ty++) {
              for (int tx=0; tx<4; tx++) {
                unsigned char vvvv = tilesetData.ReadNextByte();
                tileData[tx*2][ty] = static_cast<unsigned char>(vvvv & 0xF);
                tileData[tx*2+1][ty] = static_cast<unsigned char>((vvvv >> 4) & 0xF);
              }
            }

            int stx = tflipx ? 1 - tilex : tilex;
            int sty = tflipy ? 1 - tiley : tiley;

            int destX = (mapx << 4) + (stx << 3);
            int destY = (mapy << 4) + (sty << 3);

            bool reallyFlipX = (sflipx[tiley][tilex] ^ tflipx);
            bool reallyFlipY = (sflipy[tiley][tilex] ^ tflipy);

            Magick::PixelPacket* pixels = view.get(destX,destY,8,8);

            for (int ty=0; ty<8; ty++) {
              int actualTy = reallyFlipY ? (7-ty) : ty;

              for (int tx=0; tx<8; tx++) {
                int actualTx = reallyFlipX ? (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();
          }
        }
      }
    }
  }

  image.magick("png");
  image.write("out.png");
}