他人の空似自作物置場

touhouSE_th145BGMOnly.zip/touhouSE_src/th135_image.cpp


#include "stdafx.h"

namespace TouhouSE {

namespace TH135 {

namespace {

void error(const boost::filesystem::path &path, const unsigned int id) {
  std::wcout << boost::wformat(L"Error: 不明なTFBM形式です、code=%d(%s)。\n") % id % path;
  std::cin.get();
}

boost::filesystem::path createPalettePath1(const boost::filesystem::path &path) {
  boost::filesystem::path origFilenameBase = path.filename();
  origFilenameBase.replace_extension();
  const std::wstring &baseStr = origFilenameBase.wstring();
  const std::locale loc;
  const std::wstring::const_iterator findIt = std::find_if(baseStr.begin(), baseStr.end(), [&loc](const wchar_t c) {return std::isdigit(c, loc); });
  if (findIt == baseStr.end()) {
    return{};
  }
  return path.parent_path() / (boost::wformat(L"palette%s.bmp") % std::wstring(findIt, baseStr.end())).str();
}
boost::filesystem::path createPalettePath2(const boost::filesystem::path &path) {
  return path.parent_path() / L"palette000.bmp";
}
unsigned int findPath(const boost::filesystem::path &path, ExtractorBase &extractor) {
  if (path.empty()) {
    return UINT_MAX;
  }
  for (unsigned int i = 0; i < extractor.GetSize(); i++) {
    if (extractor.GetFileName(i) == path) {
      return i;
    }
  }
  return UINT_MAX;
}
unsigned int searchPalettePath1(const boost::filesystem::path &path, ExtractorBase &extractor) {
  return findPath(createPalettePath1(path), extractor);
}
unsigned int searchPalettePath2(const boost::filesystem::path &path, ExtractorBase &extractor) {
  return findPath(createPalettePath2(path), extractor);
}
unsigned int searchPalettePath(const boost::filesystem::path &path, ExtractorBase &extractor) {
  const unsigned int result1 = searchPalettePath1(path, extractor);
  if (result1 != UINT_MAX) {
    return result1;
  }
  return searchPalettePath2(path, extractor);
}

std::vector<Color> loadPalette(const std::vector<unsigned char> &data) {
  const boost::iostreams::array_source source(reinterpret_cast<const char *>(&data.front()), reinterpret_cast<const char *>(&data.back() + 1));
  boost::iostreams::stream<boost::iostreams::array_source> in(source);
  return readPalette(in, data.size());
}
std::vector<Color> loadPalette(const boost::filesystem::path &path) {
  boost::filesystem::ifstream ifs(path, std::ios::binary);
  std::vector<unsigned char> data(static_cast<unsigned int>(boost::filesystem::file_size(path)));
  if (data.empty()) {
    return{};
  }
  ifs.read(reinterpret_cast<char *>(&data.front()), data.size());
  if (!ifs.good()) {
    return{};
  }
  ifs.close();
  Png png;
  if (!TouhouSE::PNG::FromPng(data, png) || png.w != 16 || png.h != 16 || png.cn != 32) {
    return{};
  }
  return std::move(png.col);
}

std::vector<Color> loadPaletteFromExtractor(const boost::filesystem::path &path, ExtractorBase &extractor) {
  const unsigned int index = searchPalettePath(path, extractor);
  if (index == UINT_MAX) {
    return{};
  }
  std::vector<unsigned char> paletteData;
  if (!extractor.Extract(index, paletteData)) {
    return{};
  }
  return loadPalette(paletteData);
}

std::vector<Color> loadPaletteFromFile(const boost::filesystem::path &path) {
  const auto toPng = [](boost::filesystem::path path) {path.replace_extension(".png"); return path; };
  std::vector<boost::filesystem::path> pathList;
  pathList.push_back(createPalettePath1(path));
  pathList.push_back(toPng(pathList.back()));
  pathList.push_back(createPalettePath2(path));
  pathList.push_back(toPng(pathList.back()));
  for (const boost::filesystem::path &palettePath : pathList) {
    if (boost::filesystem::is_regular_file(palettePath)) {
      return loadPalette(palettePath);
    }
  }
  return{};
}

std::vector<Color> loadPalette(const boost::filesystem::path &path, ExtractorBase &extractor) {
  const std::vector<Color> palette1 = loadPaletteFromExtractor(path, extractor);
  if (!palette1.empty()) {
    return std::move(palette1);
  }
  return loadPaletteFromFile(path);
}

} // anonymouse

namespace endian = boost::spirit::endian;

class Th135Image : public ImageBase<Th135Image>, boost::noncopyable {
private:
#pragma pack(push, 1)
  struct Header {
    char signature[4];
    unsigned char version;
    unsigned char bitCount;
    unsigned int width; // 見た目上の幅
    unsigned int height;
    unsigned int width2; // 実際の幅、widthより広い分にはゴミデータなどが配置される
    unsigned int compSize;
  };
#pragma pack(pop)
  const Header header;
  const std::vector<unsigned char> data;
  const std::vector<Color> palette;
public:
  Th135Image(const Header &header, const std::vector<unsigned char> data, const std::vector<Color> palette)
    :header(header), data(std::move(data)), palette(std::move(palette))
  {
  }
  static boost::shared_ptr<Th135Image> Open(const boost::filesystem::path &fileName, std::istream &in, const unsigned long long int fileSize, ExtractorBase &extractor) {
    const unsigned int headerSize = sizeof(Header);
    if (fileSize < headerSize) {
      return{};
    }
    Header header;
    in.read(reinterpret_cast<char *>(&header), sizeof(Header));
    if (!in.good() || !std::equal(&header.signature[0], &header.signature[4], "TFBM") || header.width2 < header.width
      || header.version != 0 || header.bitCount % 8 != 0 || header.compSize + headerSize != fileSize
      || (header.bitCount != 32 && header.bitCount != 8 && header.bitCount != 16 && header.bitCount != 24))
    {
      return{};
    }
    const unsigned int origSize = header.width2 * header.height * (header.bitCount / 8);
    // 圧縮したら容量が増える場合は圧縮しない、なんてことはしていないようなのでチェックしない
    std::vector<unsigned char> temp(header.compSize);
    in.read(reinterpret_cast<char *>(&temp.front()), header.compSize);
    if (!in.good()) {
      error(fileName, 3);
      return{};
    }
    std::vector<unsigned char> data(origSize);
    uLongf origSizeResult = origSize;
    if (Z_OK != ::uncompress(&data.front(), &origSizeResult, &temp.front(), temp.size()) || origSizeResult != origSize) {
      error(fileName, 4);
      return{};
    }
    std::vector<Color> palette;
    if (header.bitCount == 8) {
      palette = loadPalette(fileName, extractor);
      if (palette.empty()) {
        static bool errorViewFlag = true;
        if (errorViewFlag) {
          std::wcout << L"Error: パレットの読み込みに失敗しました、いくつかのファイルが無変換で出力されます。\n";
          std::wcout << L"Tips: 別ファイルにパレットが含まれている場合、そちらを展開した上に展開すると動作する場合があります。\n";
          errorViewFlag = false;
        }
        return{};
      }
    }
    return boost::make_shared<Th135Image>(header, std::move(data), std::move(palette));
  }
  bool CreateRGBAArray(std::vector<Color> &result) const {
    const std::vector<Color> temp = readColor(data.begin(), header.bitCount, header.width2 * header.height, &palette);
    if (header.width == header.width2) {
      result = std::move(temp);
      return true;
    }
    result.resize(header.width * header.height);
    std::vector<Color>::iterator outIt = result.begin();
    std::vector<Color>::const_iterator inIt = temp.begin();
    const unsigned int skipSize = header.width2 - header.width;
    for (unsigned int h = 0; h < header.height; h++) {
      for (unsigned int w = 0; w < header.width; w++) {
        *outIt = *inIt;
        outIt++;
        inIt++;
      }
      inIt += skipSize;
    }
    return true;
  }
  unsigned int GetWidth() const {
    return header.width;
  }
  unsigned int GetHeight() const {
    return header.height;
  }
};

class Th135ImageConvertor : public ImageConverterBase<Th135Image> { };

ADD_FILE_CONVERTER(Th135ImageConvertor);

} // TH135

} // TouhouSE