他人の空似自作物置場

touhouSE.zip/touhouSE_src/dat_utility.cpp

#include "stdafx.h"

namespace DatUtility {

namespace {

#pragma warning(push)
#pragma warning(disable: 4512)
struct FindListExt {
	FindListExt(const char *ext) : ext(ext) { }
	const char * const ext;
	bool operator()(std::shared_ptr<LIST> file) const {
		const char * const file_ext = ::PathFindExtensionA(file->fn);
		return (::strcmp(ext, file_ext) == 0);
	}
};
#pragma warning(pop)

bool ThAnmImgCheck(const std::vector<unsigned char> &data) {
	BOOST_ASSERT(!data.empty());
	const unsigned char *cur = &data.front();
	while(true) {
		if(data.size() < static_cast<unsigned int>(cur - &data.front()) + 0x28) {
			return false;
		}
		const unsigned int image_filename_addr = *reinterpret_cast<const unsigned int *>(&cur[0x10]);
		if(data.size() <= image_filename_addr) {
			return false;
		}
		const char * const image_filename = reinterpret_cast<const char *>(cur) + image_filename_addr;
		if(strlen(image_filename) > 251 || strlen(image_filename) < 1 || data.size() < image_filename_addr + strlen(image_filename) + 1) {
			return false;
		}
		const unsigned int image_addr = *reinterpret_cast<const unsigned int *>(&cur[0x1C]);
		if(strcmp(&image_filename[strlen(image_filename)-4], ".png") == 0) {
			const unsigned char * const image_ptr = cur + image_addr;
			if(data.size() < static_cast<unsigned int>(image_ptr - &data.front()) + 16) {
				return false;
			}
			if(memcmp(image_ptr, "THTX", 4) != 0) {
				return false;
			}
			const unsigned int type = *reinterpret_cast<const unsigned short *>(&image_ptr[6]);
			const unsigned int w = *reinterpret_cast<const unsigned short *>(&image_ptr[8]);
			const unsigned int h = *reinterpret_cast<const unsigned short *>(&image_ptr[10]);
			const unsigned int len = *reinterpret_cast<const unsigned int*>(&image_ptr[12]);
			unsigned int color_num;
			if(type == 1){
				color_num = 4;
			} else if(type == 5){
				color_num = 2;
			} else if(type == 3){
				color_num = 2;
			} else if(type == 7) {
				color_num = 1;
			} else {
				return false;
			}
			if(data.size() < image_ptr - &data.front() + 16 + w * h * color_num) {
				return false;
			}
			if(w * h * color_num != len) {
				return false;
			}
		} else {
      if (image_addr != 0 || image_filename[0] != '@') {
        return false;
      }
    }
		if(*reinterpret_cast<const unsigned int *>(&cur[0x24])==0) {
			break;
		}
		cur += *reinterpret_cast<const unsigned int *>(&cur[0x24]);
	}
	return true;
}

} // anonymous

unsigned int CalcKeyIndex(std::shared_ptr<const LIST> file_data) {
	BOOST_ASSERT(file_data);
  return CalcKeyIndex(file_data->fn, ::strlen(file_data->fn));
}

unsigned int CalcKeyIndex(const char * const name, const unsigned int length) {
	unsigned int result = 0;
	for(unsigned int i = 0; i < length; i++) {
		result += static_cast<unsigned char>(name[i]);
	}
	return (result & 7);
}

std::shared_ptr<const LIST> ExtFindFile(const std::vector<std::shared_ptr<LIST> > &file_list, const char *ext) {
	std::shared_ptr<const LIST> result;
	std::vector<std::shared_ptr<LIST> >::const_iterator it = std::find_if(file_list.begin(), file_list.end(), FindListExt(ext));
	if(it != file_list.end()) {
		result = *it;
	}
	return result;
}

bool TryVerFileExtract(DAT_VER dat_ver, const std::vector<std::shared_ptr<LIST> > &file_list, std::shared_ptr<FILE> fp) {
	std::shared_ptr<const LIST> file_data = ExtFindFile(file_list, ".ver");
	if(!file_data) {
		return false;
	}

	std::vector<unsigned char> ver_data;
	const bool extract_result = Extract(ver_data, dat_ver, file_data, fp);
	if(!extract_result || ver_data.size() != file_data->size) {
		return false;
	}

	for(unsigned int i = 0; i < ver_data.size(); i++) {
		if(!isspace(ver_data[i]) && !isalnum(ver_data[i])) {
			return false;
		}
	}
	return true;
}

bool FindFile(const std::vector<std::shared_ptr<LIST> > &file_list, const char * const name, std::shared_ptr<LIST> &result) {
  for (const std::shared_ptr<LIST> file : file_list) {
    if (::strncmp(file->fn, name, sizeof(file->fn)) == 0) {
      result = file;
      return true;
    }
  }
  return false;
}

bool TryAnmFileExtractImpl(const DAT_VER dat_ver, const std::shared_ptr<LIST> file, const std::shared_ptr<FILE> fp) {
  std::vector<unsigned char> img_data;
  const bool extract_result = Extract(img_data, dat_ver, file, fp);
  if (!extract_result || img_data.empty()) {
    return false;
  }
  if (!ThAnmImgCheck(img_data)) {
    return false;
  }
  return true;
}

bool TryAnmFileExtractSingle(const DAT_VER dat_ver, const std::vector<std::shared_ptr<LIST> > &file_list, const std::shared_ptr<FILE> fp, const char * const name) {
  std::shared_ptr<LIST> file;
  if (!FindFile(file_list, name, file)) {
    // 該当ファイルを含まない場合は成功として扱う
    return true;
  }
  if (!TryAnmFileExtractImpl(dat_ver, file, fp)) {
    return false;
  }
  return true;
}

bool TryAnmFileExtract(const DAT_VER dat_ver, const std::vector<std::shared_ptr<LIST> > &file_list, const std::shared_ptr<FILE> fp) {
	bool check_list[8];
	for(unsigned int i = 0; i < 8; i++) {
		check_list[i] = false;
	}
	unsigned int hit = 0;
	for(unsigned int id = 0; id < file_list.size() && hit < 8; id++) {
		const char * const file_ext = ::PathFindExtensionA(file_list[id]->fn);
		if(strcmp(file_ext, ".anm") != 0) {
			continue;
		}
		const unsigned int key_index = CalcKeyIndex(file_list[id]);
		if(check_list[key_index]) {
			continue;
		}
    if (!TryAnmFileExtractImpl(dat_ver, file_list[id], fp)) {
      return false;
    }
		check_list[key_index] = true;
		hit++;
	}
  const char * const list[] = {"title.anm"};
  for (const char * const name : list) {
    if (!TryAnmFileExtractSingle(dat_ver, file_list, fp, name)) {
      return false;
    }
  }
	return true;
}

} // DatUtility