void LevelVisualGraphicSceneLayer::drawMetatileWithPriority(Graphic& dst,
                              const VRAMCache& vramCache_,
                              MetatileStructure metatileStructure,
                              int baseDrawX,
                              int baseDrawY,
                              PriorityLevel tilePriority) {
  drawTileIfSamePriority(dst, vramCache_, metatileStructure.upperLeft(),
                         baseDrawX, baseDrawY,
                         tilePriority);
  drawTileIfSamePriority(dst, vramCache_, metatileStructure.upperRight(),
                         baseDrawX + GGTile::width, baseDrawY,
                         tilePriority);
  drawTileIfSamePriority(dst, vramCache_, metatileStructure.lowerLeft(),
                         baseDrawX, baseDrawY + GGTile::height,
                         tilePriority);
  drawTileIfSamePriority(dst, vramCache_, metatileStructure.lowerRight(),
                         baseDrawX + GGTile::width,
                         baseDrawY + GGTile::height,
                         tilePriority);
}
void LevelVisualGraphicSceneLayer::drawMetatileVisual(
                               Graphic& metatileGraphic,
                               const VRAMCache& src,
                               MetatileStructure metatileStructure) {
  drawTile(metatileGraphic,
           src,
           metatileStructure.upperLeft(),
           0, 0);
  drawTile(metatileGraphic,
           src,
           metatileStructure.upperRight(),
           GGTile::width, 0);
  drawTile(metatileGraphic,
           src,
           metatileStructure.lowerLeft(),
           0, GGTile::height);
  drawTile(metatileGraphic,
           src,
           metatileStructure.lowerRight(),
           GGTile::width, GGTile::height);
}
int MetatileStructureSet::readFromData(LoadedROM& rom,
                                               Taddress address__) {
  address_ = address__;
  
  // Count of read bytes
  int byteCount = 0;
                                               
  // Get bank number of the table
  int tableBankNum = LoadedROM::directToBankNum(address__);
  
  // Compute table size.
  // The first address in the index always points to the first
  // structure definition
  Taddress contentStartBankedAddress = ByteConversion::fromBytes(
        rom.directRead(address__),
        ByteSizes::uint16Size,
        EndiannessTypes::little,
        SignednessTypes::nosign);
  
  // Convert to direct address
  Taddress contentStartAddress = 0;
  if (contentStartBankedAddress == invalidStructureToken) {
    contentStartAddress = invalidRepairAddress;
  }
  else {
    contentStartAddress = LoadedROM::getDirectAddress(
                                              tableBankNum,
                                              contentStartBankedAddress);
  }
  
  // Get size of index, given by ((tableStart - indexStart) / entrySize)
  int indexEntries = (contentStartAddress - address__) / ByteSizes::uint16Size;
  
  // Read the index.
  // Remember the highest index we encounter
  int highestIndex = 0;
  for (int i = 0; i < indexEntries; i++) {
    // Get banked address
    Taddress entryBankedAddress = ByteConversion::fromBytes(
          rom.directRead(address__ + (i * ByteSizes::uint16Size)),
          ByteSizes::uint16Size,
          EndiannessTypes::little,
          SignednessTypes::nosign);
    byteCount += ByteSizes::uint16Size;
    
    // Convert to direct address
    int entryAddress = 0;
    // If address is invalid, repair
    if (entryBankedAddress == invalidStructureToken) {
//      std::cout << contentStartAddress << std::endl;
      entryAddress = invalidRepairAddress
                             + (i * MetatileStructure::dataSize);
    }
    else {
      entryAddress = LoadedROM::getDirectAddress(
                                        tableBankNum,
                                        entryBankedAddress);
    }
    
    // Compute the actual index this address corresponds to
    int structureIndex = (entryAddress - contentStartAddress)
          / MetatileStructure::dataSize;
    
    // Add mapping to index map
    index_.insert(MetatileIndexToStructurePair(i, structureIndex));
    
    if (structureIndex > highestIndex) {
      highestIndex = structureIndex;
    }
  }
  
  // Increment highest index (we're using it as a limit for the read loop)
  ++highestIndex;
  
  // Read the structure definitions.
  // The highest index indicates the actual number of structure definitions
  // in the table
  for (int i = 0; i < highestIndex; i++) {
    // Read each metatile
    MetatileStructure metatile;
    metatile.readFromData(rom.directRead(contentStartAddress
                            + (i * MetatileStructure::dataSize)));
    
    // If reading invalid table, repair tiles
    if (contentStartAddress == invalidRepairAddress) {
//      std::cout << highestIndex << std::endl;
      metatile.upperLeft() = TileReference(invalidRepairTile);
      metatile.upperRight() = TileReference(invalidRepairTile);
      metatile.lowerLeft() = TileReference(invalidRepairTile);
      metatile.lowerRight() = TileReference(invalidRepairTile);
    }
    
    // Add to content
    primaryStorage_.push_back(metatile);
    
    byteCount += MetatileStructure::dataSize;
  }
  
  // HACK: (sort of) Add dummy entries to fill rest of set.
  // This fixes issues that I think are caused by out-of-range
  // values in the index.
//  while (primaryStorage_.size() < 256) {
//    primaryStorage_.push_back(MetatileStructure());
//  }
  
  return byteCount;
}
int MetatileStructureSet::load(const Tbyte* data) {
  // Count of bytes written
  int byteCount = 0;
  
  // Clear existing storage
  primaryStorage_.clear();
  index_.clear();
  
  // Read start address
  address_ = ByteConversion::fromBytes(data + byteCount,
                                       ByteSizes::uint32Size,
                                       EndiannessTypes::little,
                                       SignednessTypes::nosign);
  byteCount += ByteSizes::uint32Size;
  
  // Read number of index entries
  int numIndexEntries = ByteConversion::fromBytes(data + byteCount,
                                                  ByteSizes::uint16Size,
                                                  EndiannessTypes::little,
                                                  SignednessTypes::nosign);
  byteCount += ByteSizes::uint16Size;
  
  // Read index entries
  for (int i = 0; i < numIndexEntries; i++) {
    // Read index number
    int indexNum = ByteConversion::fromBytes(data + byteCount,
                                             ByteSizes::uint16Size,
                                             EndiannessTypes::little,
                                             SignednessTypes::nosign);
    byteCount += ByteSizes::uint16Size;
  
    // Read mapping
    int structureNum = ByteConversion::fromBytes(data + byteCount,
                                             ByteSizes::uint16Size,
                                             EndiannessTypes::little,
                                             SignednessTypes::nosign);
    byteCount += ByteSizes::uint16Size;
    
    // Add to index
    index_.insert(MetatileIndexToStructurePair(indexNum,
                                               structureNum));
  }
  
  // Read number of metatile structure definitions
  int numStructureEntries = ByteConversion::fromBytes(
                                                  data + byteCount,
                                                  ByteSizes::uint16Size,
                                                  EndiannessTypes::little,
                                                  SignednessTypes::nosign);
  byteCount += ByteSizes::uint16Size;
  
//  std::cout << numStructureEntries << std::endl;
  
  // Read metatile structure definitions
  for (int i = 0; i < numStructureEntries; i++) {
    // Read structure definition
    MetatileStructure metatile;
    metatile.readFromData(data + byteCount);
    
    // Add to count
    byteCount += MetatileStructure::dataSize;
    
    // Add structure definition to table
    primaryStorage_.push_back(metatile);
  }
  
  // Add dummy entries to fill up set
//  while (primaryStorage_.size() < 256) {
//    primaryStorage_.push_back(MetatileStructure());
//  }

  return byteCount;
}