他人の空似自作物置場

touhouSE_th145BGMOnly.zip/touhouSE_src/th11_anm.cpp


#include "stdafx.h"

namespace TouhouSE {

namespace TH11_ANM {

namespace endian = boost::spirit::endian;

class FileRecord {
private:
  class Header : boost::noncopyable {
  public:
    endian::ulittle32_t unknown1;
    endian::ulittle16_t region_count;
    endian::ulittle16_t attr_count;
    endian::ulittle16_t unknown2;
    endian::ulittle16_t width;
    endian::ulittle16_t height;
    endian::ulittle16_t type;
    endian::ulittle32_t name_offset;
    endian::ulittle16_t left;
    endian::ulittle16_t top;
    endian::ulittle32_t unknown3;
    endian::ulittle32_t image_offset;
    endian::ulittle32_t unknown4;
    endian::ulittle32_t next_offset;
    endian::ulittle8_t unknown5[0x18];
  };

  FileRecord(const std::string name, const unsigned int addr, const boost::shared_ptr<const Header> header) :
    name(name), addr(addr), header(header)
  {}

public:
  const std::string name;
  const unsigned int addr;
  const boost::shared_ptr<const Header> header;

  static bool Read(std::istream &in, const unsigned long long int file_size, std::vector<FileRecord> &result) {
    if (!in.good()) {
      return false;
    }
    boost::shared_ptr<Header> header;
    unsigned int base_addr = 0;
    do {
      header = boost::make_shared<Header>();
      in.seekg(base_addr);
      if (!in.good() || file_size < base_addr + sizeof(*header)) {
        return false;
      }
      in.read(reinterpret_cast<char *>(header.get()), sizeof(*header));
      if (!in.good() || file_size < base_addr + header->name_offset || file_size < base_addr + header->next_offset
        || file_size < base_addr + header->image_offset || header->unknown1 != 0x00000008 || header->unknown2 != 0
        || std::count(&header->unknown5[0], &header->unknown5[_countof(header->unknown5)], 0) != _countof(header->unknown5)) {
        return false;
      }
      std::string name;
      in.seekg(base_addr + header->name_offset);
      if (!in.good()) {
        return false;
      }
      while (true) {
        char c;
        in.read(&c, sizeof(c));
        if (!in.good()) {
          return false;
        }
        if (c == '\0') {
          break;
        }
        name.push_back(c);
      }
      if (boost::filesystem::path(name).extension() == ".png") {
        result.push_back(FileRecord(name, base_addr, header));
      }
      base_addr += header->next_offset;
    } while(header->next_offset > 0);
    return true;
  }
};

class TouhouTexture: boost::noncopyable {
private:
  class Header : boost::noncopyable {
  public:
    char signature[4];
    endian::ulittle16_t unknown; // 常に0
    endian::ulittle16_t type;
    endian::ulittle16_t width;
    endian::ulittle16_t height;
    endian::ulittle32_t length;
  };

public:
  std::vector<Color> data;
  unsigned int width;
  unsigned int height;

  bool Read(std::istream &in, const unsigned long long int file_size, const FileRecord &record) {
    if (!in.good() || file_size < record.addr + record.header->image_offset + sizeof(Header)) {
      return false;
    }
    in.seekg(record.addr + record.header->image_offset);
    boost::shared_ptr<Header> header = boost::make_shared<Header>();
    in.read(reinterpret_cast<char *>(header.get()), sizeof(*header));
    if (!in.good() || file_size < record.addr + record.header->image_offset + sizeof(Header) + header->length
      || !std::equal(header->signature, &header->signature[sizeof(header->signature)], "THTX")
      || (header->length == 0 && (header->width != 0 || header->height != 0))
      || (header->length > 0 && (header->width == 0 || header->height == 0))
    ) {
      return false;
    }
    if (header->length == 0) {
      data.clear();
      return true;
    }
    std::vector<unsigned char> raw_data(header->length);
    in.read(reinterpret_cast<char *>(&raw_data.front()), raw_data.size());
    width = header->width;
    height = header->height;
    switch(header->type) {
    case 1:
      return ReadImage1(raw_data);
    case 3:
      return ReadImage3(raw_data);
    case 5:
      return ReadImage5(raw_data);
    case 7:
      return ReadImage7(raw_data);
    default:
      return false;
    }
  }

  bool ReadImage1(const std::vector<unsigned char> &in) {
    const unsigned int pixel_count = width * height;
    if (in.size() != pixel_count * 4) {
      return false;
    }
    data.resize(pixel_count);
    for (unsigned int i = 0; i < pixel_count; i++) {
      data[i].b     = in[i * 4 + 0];
      data[i].g     = in[i * 4 + 1];
      data[i].r     = in[i * 4 + 2];
      data[i].alpha = in[i * 4 + 3];
    }
    return true;
  }

  bool ReadImage3(const std::vector<unsigned char> &in) {
    const unsigned int pixel_count = width * height;
    if (in.size() != pixel_count * 2) {
      return false;
    }
    data.resize(pixel_count);
    for (unsigned int i = 0; i < pixel_count; i++) {
      data[i].b     = ((in[i * 2 + 0] & 0x1F) >> 0) * 0xFF / 0x1F;
      data[i].g     = ((*reinterpret_cast<const unsigned short *>(&in[i * 2 + 0]) & 0x07E0) >> 5) * 0xFF / 0x3F;
      data[i].r     = ((in[i * 2 + 1] & 0xF8) >> 3) * 0xFF / 0x1F;
      data[i].alpha = 0xFF;
    }
    return true;
  }

  bool ReadImage5(const std::vector<unsigned char> &in) {
    const unsigned int pixel_count = width * height;
    if (in.size() != pixel_count * 2) {
      return false;
    }
    data.resize(pixel_count);
    for (unsigned int i = 0; i < pixel_count; i++) {
      data[i].b     = ((in[i * 2 + 0] & 0x0F) >> 0) * 0xFF / 0x0F;
      data[i].g     = ((in[i * 2 + 0] & 0xF0) >> 4) * 0xFF / 0x0F;
      data[i].r     = ((in[i * 2 + 1] & 0x0F) >> 0) * 0xFF / 0x0F;
      data[i].alpha = ((in[i * 2 + 1] & 0xF0) >> 4) * 0xFF / 0x0F;
    }
    return true;
  }

  bool ReadImage7(const std::vector<unsigned char> &in) {
    const unsigned int pixel_count = width * height;
    if (in.size() != pixel_count) {
      return false;
    }
    data.resize(pixel_count);
    for (unsigned int i = 0; i < pixel_count; i++) {
      data[i].b = data[i].g = data[i].r = in[i];
      data[i].alpha = 0xFF;
    }
    return true;
  }

  bool ToPng(std::vector<unsigned char> &result) {
    return PNG::ToPng(width, height, data, result);
  }
};

class Owner : public ExtractorBase {
private:
	std::istream &in;
	const unsigned long long int file_size;
	const boost::shared_ptr<const std::vector<FileRecord> > file_list;

  Owner(std::istream &in, const unsigned long long int file_size, const boost::shared_ptr<const std::vector<FileRecord> > file_list) :
    in(in), file_size(file_size), file_list(file_list)
  {
  }

public:
  static boost::shared_ptr<Owner> Open(std::istream &in, const unsigned long long int file_size) {
    boost::shared_ptr<Owner> result;

    const boost::shared_ptr<std::vector<FileRecord> > list = boost::make_shared<std::vector<FileRecord> >();
    if (!FileRecord::Read(in, file_size, *list)) {
      return result;
    }
    result.reset(new Owner(in, file_size, list));
    return result;
  }

  bool Extract(const unsigned int index, std::vector<unsigned char> &result) {
    const FileRecord &record = file_list->at(index);
    TouhouTexture texture;
    if (!texture.Read(in, file_size, record)) {
      return false;
    }
    return texture.ToPng(result);
  }

  unsigned int GetSize() const {
    return file_list->size();
  }

  std::wstring GetName() const {
    return L"東方地霊殿アニメーション定義ファイル";
  }

  boost::filesystem::path GetFileName(unsigned int index) const {
    return file_list->at(index).name;
  }
};


ADD_DAT_EXTRACTOR(Owner);

} // TH11_ANM

} // TouhouSE