SSブログ

某データ展開用Susieプラグイン [投げっぱなしツール]

某ゲームで使われていたアーカイブデータを展開するSusieプラグインです。

同様のプラグインが存在していたはずなのですが、配布サイトが閉鎖していたため、自分で作ってみました。
といっても、圧縮も暗号化も全くしていないデータなので、アーカイブ型のSusieプラグインの練習も兼ねての習作です。

大した理由はないけれど、公開はソースコードのみ。
使いたい人は、各自、コンパイルしてください。

「コンパイルの方法を詳しく説明しろ」とか「コンパイル済みのバイナリデータを寄越せ」といった要望には、一切お答えしません。
ちなみに、Visual C++ 7.1 (Visual Studio .NET 2003)ではコンパイル出来たのを確認。

「某ゲームって、なんじゃー」という質問にも答えませんので、親切な人が「○○では?」とコメント欄で指摘してくれるのを期待しましょう。

ところで、GetFile API にメモリ上のイメージを渡した時の処理って、おかしくないかな?っていうか、おかしいよね?
#include <windows.h>
#include <time.h>
#include <memory>

#pragma pack(push,1)
// Susieプラグインが扱うファイル情報
typedef struct
{
    unsigned char method[8];    // 圧縮法の種類
    unsigned long position;     // ファイル上での位置
    unsigned long compsize;     // 圧縮されたサイズ
    unsigned long filesize;     // 元のファイルサイズ
    time_t timestamp;           // ファイルの更新日時
    char path[200];             // 相対パス
    char filename[200];         // ファイルネーム
    unsigned long crc;          // CRC
} fileInfo;

// データファイルのヘッダ部分にあるファイル情報
struct PackFileInfo {
    char path[0x20];            // ファイル名
    unsigned long offset;       // ファイル先頭からの位置
    unsigned long unknown;      // 不明(ファイルの種類…?)
    unsigned long size;         // ファイルサイズ
    unsigned long size0;        // ファイルサイズ
};
// sizeとsize0の、どっちかがオリジナルサイズで、もう一方が圧縮サイズ?
#pragma pack(pop)

// データファイルヘッダ部のサイズ
static const int PACKDAT_HEADER_SIZE = 0x10;

// ===================================================================

// ファイル出力クラス
class FileWriter
{
  private:
    HANDLE handle;

  public:
    FileWriter(const char* path)
    {
        handle = ::CreateFile(path, GENERIC_WRITE, 0, NULL,
                              CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    }

    ~FileWriter()
    {
        if (handle != INVALID_HANDLE_VALUE)
            ::CloseHandle(handle);
    }

    bool write(const void* buff, unsigned long size)
    {
        if (handle == INVALID_HANDLE_VALUE)
            return false;
        
        DWORD write_byte;
        if (!::WriteFile(handle, buff, size, &write_byte, NULL))
            return false;

        return write_byte == size;
    }
};

// ファイル入力クラス
class FileReader
{
  private:
    HANDLE handle;

  public:
    FileReader(const char* path)
    {
        handle = ::CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL,
                              OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    }

    ~FileReader()
    {
        close();
    }

    void close()
    {
        if (handle != INVALID_HANDLE_VALUE)
            ::CloseHandle(handle);
    }

    bool seek(long pos)
    {
        if (handle == INVALID_HANDLE_VALUE)
            return false;
        return ::SetFilePointer(handle, pos, NULL, FILE_BEGIN) != (DWORD)-1;
    }
    
    bool read(void* buff, unsigned long size)
    {
        if (handle == INVALID_HANDLE_VALUE)
            return false;

        DWORD read_byte;
        if (!::ReadFile(handle, buff, size, &read_byte, NULL))
            return false;

        return read_byte == size;
    }
};

// データファイルのファイル情報を読むクラス
class FileInfoReader
{
  public:
    virtual ~FileInfoReader() {}

    virtual const PackFileInfo* next(void*) = 0;
    virtual bool toMemory(unsigned long, unsigned long, void*) = 0;
    virtual bool toFile(unsigned long, unsigned long, const char*) = 0;
};

// ファイルからデータファイルのファイル情報を読むクラス
class FIRFile : public FileInfoReader
{
  private:
    FileReader in;
    
  public:
    FIRFile(LPSTR fname, long offset) : in(fname)
    {
        if (!in.seek(offset + PACKDAT_HEADER_SIZE))
            close();
    }

    const PackFileInfo* next(void* buff)
    {
        if (in.read(buff, sizeof(PackFileInfo)))
            return reinterpret_cast<PackFileInfo*>(buff);
        else
            return NULL;
    }

    bool toMemory(unsigned long offset, unsigned long size, void* buff)
    {
        if (!in.seek(offset))
            return false;

        return in.read(buff, size);
    }

    bool toFile(unsigned long offset, unsigned long size, const char* path)
    {
        if (!in.seek(offset))
            return false;

        char buff[1024];
        DWORD read_byte;

        FileWriter out(path);

        while (size != 0) {
            unsigned long s = min(size, 1024);
            if (!in.read(buff, s))
                return false;
            if (!out.write(buff, s))
                return false;
            size -= s;
        }

        return true;
    }
};

// メモリ上のデータイメージからデータファイルのファイル情報を読むクラス
class FIRMem : public FileInfoReader
{
  private:
    const char* data;
    unsigned long current;
    unsigned long end;

  public:
    FIRMem(LPSTR buff, long len) : data(buff), current(PACKDAT_HEADER_SIZE), end(len)
    {
    }

    const PackFileInfo* next(void*)
    {
        const void* p = data + current;
        current += sizeof(PackFileInfo);

        return reinterpret_cast<const PackFileInfo*>(current <= end ? p : NULL);
    }

    bool toMemory(unsigned long offset, unsigned long size, void* buff)
    {
        if (offset + size > end)
            return false;

        ::CopyMemory(buff, data + offset, size);
        return true;
    }

    bool toFile(unsigned long offset, unsigned long size, const char* path)
    {
        if (offset + size > end)
            return false;

        FileWriter out(path);
        return out.write(data + offset, size);
    }
};

// データファイルのファイル情報からSusie用ファイル情報に変換
void copyPackFileInfo2fileInfo(fileInfo* dest, const PackFileInfo* src)
{
    ::ZeroMemory(dest, sizeof(fileInfo));
    memcpy(dest->method, "PACKDAT", 8);
    dest->position = src->offset;
    dest->compsize = src->size;
    dest->filesize = src->size;
//    dest->timestamp = 0;
//    strcpy(dest->path, "");
    strncpy(dest->filename, src->path, 0x20);
//    dest->crc = 0;;
}

// ===================================================================

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                       )
{
    switch (ul_reason_for_call)
    {
      case DLL_PROCESS_ATTACH:
      case DLL_THREAD_ATTACH:
      case DLL_THREAD_DETACH:
      case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

// ===================================================================
// ===================================================================

// Plug-inに関する情報を得る
extern "C" int WINAPI GetPluginInfo(int infono, LPSTR buf, int buflen)
{
    const char* DATA[] = {
        "00AM",
        "PACKDAT extract plug-in programmed by 依積晶紀",
        "*.dat",
        "PACKDAT archive",
    };
    const int num = sizeof(DATA)/sizeof(DATA[0]);
    if (infono < 0 || infono >= num)
        return 0;

    const int output_len = min(buflen, strlen(DATA[infono]) + 1);
    memcpy(buf, DATA[infono], output_len);

    return output_len;
}

// 展開可能な(対応している)ファイル形式か調べる。
extern "C" int WINAPI IsSupported(LPSTR filename, DWORD dw)
{
    char buff[100];

    DWORD size;
    const char* DATA;

    if (HIWORD(dw) == 0) {
        // ファイルハンドル指定
        if (!::ReadFile((HANDLE)dw, buff, sizeof(buff), &size, NULL)) {
            return 0;   // 読み込みエラーは、「非対応」と言う事で…
        }

        if (size <= PACKDAT_HEADER_SIZE + sizeof(PackFileInfo)) {
            return 0;
        }

        DATA = buff;
    } else {
        // バッファ指定
        DATA = reinterpret_cast<const char*>(dw);
        size = 1024*2;
    }

    return memcmp(DATA, "PACKDAT", 7) == 0 ? 1 : 0;
}

// アーカイブ内のすべてのファイルの情報を取得する
extern "C" int WINAPI GetArchiveInfo(LPSTR buf, long len,
                                     unsigned int flag, HLOCAL* lphInf)
{
    char buff[0x30];

    std::auto_ptr<FileInfoReader> in;
    
    switch (flag & 0x07) {
      case 0:
        in.reset(new FIRFile(buf, len));
        break;
      case 1:
        in.reset(new FIRMem(buf, len));
        break;
      default:
        return -1;
    }

    const PackFileInfo* info = in->next(buff);
    if (info == NULL)
        return 2;

    if ((info->offset - PACKDAT_HEADER_SIZE) % sizeof(PackFileInfo) != 0)
        return 2;
    const int num = (info->offset - PACKDAT_HEADER_SIZE) / sizeof(PackFileInfo);

    HLOCAL hInf = ::LocalAlloc(LHND, (num + 1)*sizeof(fileInfo));
    if (hInf == NULL)
        return 4;

    fileInfo* pInf = reinterpret_cast<fileInfo*>(::LocalLock(hInf));
    if (pInf == NULL) {
        ::LocalFree(hInf);
        return 5;
    }

    copyPackFileInfo2fileInfo(pInf, info);
    pInf++;

    for (int i = 1; i < num; i++, pInf++) {
        info = in->next(buff);
        if (info == NULL) {
            ::LocalUnlock(hInf);
            ::LocalFree(hInf);
            return 3;
        }
        copyPackFileInfo2fileInfo(pInf, info);
    }

    ::ZeroMemory(pInf, sizeof(fileInfo));
    
    ::LocalUnlock(hInf);
    *lphInf = hInf;
    return 0;
}

// アーカイブ内の指定したファイルの情報を取得する
extern "C" int WINAPI GetFileInfo(LPSTR buf, long len, LPSTR filename,
                                  unsigned int flag, fileInfo *lpInfo)
{
    int (*CompareFunction)(const char*,const char*);
    CompareFunction = (flag & 0x0080) == 0 ? strcmp : stricmp;

    HANDLE hInf;
    int ret = GetArchiveInfo(buf, len, flag, &hInf);
    if (ret != 0)
        return ret;

    ret = 8;
    const fileInfo* pInf = reinterpret_cast<fileInfo*>(::LocalLock(hInf));
    while (pInf->method[0] != '\0') {
        if (CompareFunction(pInf->filename, filename) == 0) {
            memcpy(lpInfo, pInf, sizeof(fileInfo));
            ret = 0;
            break;
        }
        pInf++;
    }
    
    ::LocalUnlock(hInf);
    ::LocalFree(hInf);

    return ret;
}

// アーカイブ内のファイルを取得する
extern "C" int WINAPI GetFile(LPSTR src, long len, LPSTR dest,
                              unsigned int flag, FARPROC prgressCallback,
                              long lData)
{
    char buff[0x30];
    std::auto_ptr<FileInfoReader> in;
    
    switch (flag & 0x07) {
      case 0:
        in.reset(new FIRFile(src, 0));
        break;
      case 1:
        in.reset(new FIRMem(src, len));
        break;
      default:
        return -1;
    }
   
    const PackFileInfo* info = in->next(buff);
    if (info == NULL)
        return 2;

    if ((info->offset - PACKDAT_HEADER_SIZE) % sizeof(PackFileInfo) != 0)
        return 2;
    const int num = (info->offset - PACKDAT_HEADER_SIZE) / sizeof(PackFileInfo);

    unsigned long offset = 0;
    unsigned long size = 0;
    const char* fname = NULL;

    if (info->offset == len) {
        offset = info->offset;
        size = info->size;
        fname = info->path;
    } else {
        for (int i = 1; i < num; i++) {
            info = in->next(buff);
            if (info == NULL)
                return -1;

            if (info->offset == len) {
                offset = info->offset;
                size = info->size;
                fname = info->path;
                break;
            }
        }
    }
    if (fname == NULL)
        return -1;

    if ((flag & 0x0700) == 0) {
        // ファイルに出力
        char path[MAX_PATH];
        sprintf(path, "%s\\%s", dest, fname);

        if (!in->toFile(offset, size, path)) {
            return 6;
        }
        return 0;
        
    } else if  ((flag & 0x0700) == 0x0100) {
        // メモリに出力
        HANDLE hBuf = ::LocalAlloc(LHND, size);
        if (hBuf == NULL)
            return 4;

        void* pBuf = ::LocalLock(hBuf);
        if (pBuf == NULL) {
            ::LocalFree(hBuf);
            return 5;
        }

        if (!in->toMemory(offset, size, pBuf)) {
            ::LocalUnlock(hBuf);
            ::LocalFree(hBuf);
            return 6;
        }

        ::LocalUnlock(hBuf);
        *((HANDLE*)dest) = hBuf;
        return 0;

    } else {

        return -1;
    }
}
[新幹線] 今日の一冊
マンガ幾何入門―頭脳が楽しく鍛えられる (ブルーバックス)

マンガ幾何入門―頭脳が楽しく鍛えられる

  • 作者: 岡部 恒治
  • 出版社/メーカー: 講談社
  • 発売日: 1996/09
  • メディア: 新書

タグ:VisualC++
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。