Skip to content

Commit

Permalink
Complete API changing.
Browse files Browse the repository at this point in the history
Everything should work now.

Fixed V3 decryption which gives invalid output for the first decrypted byte.
  • Loading branch information
MikuAuahDark committed Mar 11, 2017
1 parent bbd9a58 commit ccaf656
Show file tree
Hide file tree
Showing 11 changed files with 612 additions and 442 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ Basically it allows decryption of LL!SIF game files so that it can be opened wit

File decryption support
=======================
HonokaMiku supports decryption of SIF EN/WW, JP, TW, and CN game files, from version 1 encryption format to version 3 encryption format.
HonokaMiku supports decryption of SIF EN/WW, JP, TW, and CN game files, from version 1 encryption format to version 4 encryption format.

It is also possible to decrypt Playground game files not from LL!SIF if you have the MD5 prefix key by using the API
It is also possible to decrypt Playground game files not from LL!SIF if you have the MD5 prefix key by using the API.

Documentation
=============
Expand All @@ -27,4 +27,4 @@ Implementation In Other Languages
=================================
Did you rewrite HonokaMiku to other languages? Send me your work and I'll add it in here.

* [PHP Implementation](https://github.com/MikuAuahDark/SIFUniDecrypt) by [MikuAuahDark](https://github.com/MikuAuahDark)
* ~~[PHP Implementation](https://github.com/MikuAuahDark/SIFUniDecrypt) by [MikuAuahDark](https://github.com/MikuAuahDark)~~ (Correspond to HonokaMiku v4.2.1)
6 changes: 3 additions & 3 deletions VersionInfo.rc
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

#define HONOKAMIKU_VERSION_MAJOR 4
#define HONOKAMIKU_VERSION_MINOR 3
#define HONOKAMIKU_VERSION_PATCH 0
#define HONOKAMIKU_VERSION_PATCH 1
//#define HONOKAMIKU_VERSION_STRING "" _STR2(HONOKAMIKU_VERSION_MAJOR) "." _STR2(HONOKAMIKU_VERSION_MINOR) "." _STR2(HONOKAMIKU_VERSION_PATCH) ""
#define HONOKAMIKU_VERSION_STRING "5.0.0 beta-1"
#define HONOKAMIKU_VERSION_STRING_RC "5.0.0 beta-1"
#define HONOKAMIKU_VERSION_STRING "5.0.0 beta-2"
#define HONOKAMIKU_VERSION_STRING_RC "5.0.0 beta-2"
#define HONOKAMIKU_VERSION ((HONOKAMIKU_VERSION_MAJOR*100000000)+(HONOKAMIKU_VERSION_MINOR*100000)+(HONOKAMIKU_VERSION_PATCH))

#ifdef RC_INVOKED
Expand Down
28 changes: 24 additions & 4 deletions src/CN_Decrypter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,38 @@ static const uint32_t cn_key_tables[64] = {
0xcd84d15bu, 0xa0290f82u, 0xd3e95afcu, 0x9c6a97b4u
};

HonokaMiku::CN3_Dctx::CN3_Dctx(const void* header, const char* filename): HonokaMiku::V3_Dctx(HonokaMiku::GetPrefixFromGameId(7), header, filename) {}
////////////////////
// Version 2 code //
////////////////////

HonokaMiku::CN2_Dctx::CN2_Dctx(const void* header, const char* filename):V2_Dctx(GetPrefixFromGameId(5), header, filename) {}

uint32_t HonokaMiku::CN2_Dctx::get_id()
{
return HONOKAMIKU_GAMETYPE_CN | HONOKAMIKU_DECRYPT_V2;
}

////////////////////
// Version 3 code //
////////////////////

HonokaMiku::CN3_Dctx::CN3_Dctx(const void* header, const char* filename):V3_Dctx(GetPrefixFromGameId(7), header, filename) {}

const uint32_t* HonokaMiku::CN3_Dctx::_getKeyTables() { return cn_key_tables; }

void HonokaMiku::CN3_Dctx::final_setup(const char* filename, const void* block_rest, int force_version)
uint32_t HonokaMiku::CN3_Dctx::get_id()
{
return HONOKAMIKU_GAMETYPE_CN | HONOKAMIKU_DECRYPT_V3;
}

void HonokaMiku::CN3_Dctx::final_setup(const char* filename, const void* block_rest, int32_t force_version)
{
finalDecryptV3(this, 1847, filename, block_rest, force_version);
}

HonokaMiku::CN3_Dctx* HonokaMiku::CN3_Dctx::encrypt_setup(const char* filename,void* hdr_out)
HonokaMiku::CN3_Dctx* HonokaMiku::CN3_Dctx::encrypt_setup(const char* filename, void* hdr_out, int32_t fv)
{
CN3_Dctx* dctx = new CN3_Dctx;
setupEncryptV3(dctx, HonokaMiku::GetPrefixFromGameId(7), 1847, filename, hdr_out);
setupEncryptV3(dctx, HonokaMiku::GetPrefixFromGameId(7), 1847, filename, hdr_out, fv);
return dctx;
}
141 changes: 99 additions & 42 deletions src/DecrypterContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,62 @@

#include <stdint.h>

#define HONOKAMIKU_GAMETYPE_JP 0x0000
#define HONOKAMIKU_GAMETYPE_EN 0x0001
#define HONOKAMIKU_GAMETYPE_TW 0x0002
#define HONOKAMIKU_GAMETYPE_CN 0x0003

#define HONOKAMIKU_DECRYPT_V1 0x00010000
#define HONOKAMIKU_DECRYPT_V2 0x00020000
#define HONOKAMIKU_DECRYPT_V3 0x00030000
#define HONOKAMIKU_DECRYPT_V4 0x00040000
#define HONOKAMIKU_DECRYPT_V5 0x00050000
#define HONOKAMIKU_DECRYPT_V6 0x00060000
#define HONOKAMIKU_DECRYPT_V7 0x00070000

namespace HonokaMiku
{
/// \brief Gets game key prefix for specificed game id.
/// \param game_id The game ID. Valid game IDs are
/// 1. EN/WW Version 2
/// 2. JP Version 2/KR
/// 3. TW Version 2
/// 4. JP Version 3
/// 5. CN Version 2
/// 6. EN/WW Version 3
/// 7. CN Version 3
/// 8. TW Version 3
/// \returns prefix key of specificed game ID or NULL if game ID is invalid
/// \note If you just want to get prefix key, the version doesn't matter.
inline const char* GetPrefixFromGameId(char game_id)
{
switch(game_id)
{
case 1:
case 6: return "BFd3EnkcKa";
case 2:
case 4: return "Hello";
case 3:
case 8: return "M2o2B7i3M6o6N88";
case 5:
case 7: return "iLbs0LpvJrXm3zjdhAr4";
default: return NULL;
}
}

/// \brief same as GetPrefixFromGameId() except this one operates on `HONOKAMIKU_GAMETYPE_*` constants
inline const char* GetPrefixFromGameType(uint32_t gt)
{
switch(gt)
{
case HONOKAMIKU_GAMETYPE_JP: return "Hello";
case HONOKAMIKU_GAMETYPE_EN: return "BFd3EnkcKa";
case HONOKAMIKU_GAMETYPE_TW: return "M2o2B7i3M6o6N88";
case HONOKAMIKU_GAMETYPE_CN: return "iLbs0LpvJrXm3zjdhAr4";
default: return NULL;
}
}

class V2_Dctx;
class V3_Dctx;

Expand Down Expand Up @@ -55,6 +109,8 @@ namespace HonokaMiku
/// \param filename File name that want to be decrypted. This affects the key calculation.
/// \param block_rest The next 12-bytes header of Version 3 encrypted file.
virtual void final_setup(const char* filename, const void* block_rest, int32_t fv = 0) = 0;
/// \brief Function to get the decrypter filename
virtual uint32_t get_id() = 0;
protected:
inline DecrypterContext() {}
/// \brief The key update function. Used to update the key. Used internally and protected
Expand All @@ -64,21 +120,24 @@ namespace HonokaMiku
/// For encrypt_setup static members for SIF EN, TW, KR, and CN. Used internally
void setupEncryptV2(V2_Dctx* dctx, const char* prefix, const char* filename, void* hdr_out);
/// For encrypt_setup static members for Version 3. Used internally
void setupEncryptV3(V3_Dctx* dctx, const char* prefix, uint16_t name_sum_base, const char* filename, void* hdr_out);
void setupEncryptV3(V3_Dctx* dctx, const char* prefix, uint16_t name_sum_base, const char* filename, void* hdr_out, int32_t force_version = 0);
/// To finalize version 3 decrypter
void finalDecryptV3(V3_Dctx* dctx, uint32_t expected_sum_name, const char* filename, const void* block_rest, int32_t force_version = 0);

/// Base class of Version 1 decrypter/encrypter
class V1_Dctx: public DecrypterContext
{
protected:
int32_t game_ver;

inline V1_Dctx() {}
void update();
public:
/// \brief Initialize Version 1 decrypter context
/// \param key_prefix String prepended before MD5 calculation
/// \param filename File name that want to be decrypted.
V1_Dctx(const char* key_prefix, const char* filename);
uint32_t get_id();
void decrypt_block(void* buffer, uint32_t len);
void decrypt_block(void* dest, const void* src, uint32_t len);
void goto_offset(uint32_t offset);
Expand Down Expand Up @@ -134,7 +193,7 @@ namespace HonokaMiku
static V3_Dctx* encrypt_setup(const char* prefix, const unsigned int* key_tables, const char* filename, void* hdr_out);
virtual void final_setup(const char* , const void* , int ) = 0;

friend void setupEncryptV3(V3_Dctx* , const char* , uint16_t , const char* , void* );
friend void setupEncryptV3(V3_Dctx* , const char* , uint16_t , const char* , void* , int32_t );
friend void finalDecryptV3(V3_Dctx* , uint32_t , const char* , const void* , int32_t );
};

Expand All @@ -151,10 +210,11 @@ namespace HonokaMiku
/// \param filename File name that want to be decrypted. This affects the key calculation.
/// \exception std::runtime_error The header does not match and this decrypter context can't decrypt it.
JP3_Dctx(const void* header, const char* filename);
uint32_t get_id();
/// \brief Creates SIF JP decrypter context specialized for encryption.
/// \param filename File name that want to be encrypted. This affects the key calculation.
/// \param hdr_out Pointer with size of 16-bytes to store the file header.
static JP3_Dctx* encrypt_setup(const char* filename, void* hdr_out);
static JP3_Dctx* encrypt_setup(const char* filename, void* hdr_out, int32_t force_ver = 0);
void final_setup(const char* filename, const void* block_rest, int force_ver = 0);
};

Expand All @@ -170,10 +230,11 @@ namespace HonokaMiku
/// \param filename File name that want to be decrypted. This affects the key calculation.
/// \exception std::runtime_error The header does not match and this decrypter context can't decrypt it.
EN3_Dctx(const void* header, const char* filename);
uint32_t get_id();
/// \brief Creates SIF EN decrypter context specialized for encryption. (version 3)
/// \param filename File name that want to be encrypted. This affects the key calculation.
/// \param hdr_out Pointer with size of 16-bytes to store the file header.
static EN3_Dctx* encrypt_setup(const char* filename, void* hdr_out);
static EN3_Dctx* encrypt_setup(const char* filename, void* hdr_out, int32_t force_ver = 0);
void final_setup(const char* filename, const void* block_rest, int32_t force_ver = 0);
};

Expand All @@ -189,10 +250,11 @@ namespace HonokaMiku
/// \param filename File name that want to be decrypted. This affects the key calculation.
/// \exception std::runtime_error The header does not match and this decrypter context can't decrypt it.
TW3_Dctx(const void* header, const char* filename);
uint32_t get_id();
/// \brief Creates SIF TW decrypter context specialized for encryption. (version 3)
/// \param filename File name that want to be encrypted. This affects the key calculation.
/// \param hdr_out Pointer with size of 16-bytes to store the file header.
static TW3_Dctx* encrypt_setup(const char* filename, void* hdr_out);
static TW3_Dctx* encrypt_setup(const char* filename, void* hdr_out, int32_t force_ver = 0);
void final_setup(const char* filename, const void* block_rest, int32_t force_ver = 0);
};

Expand All @@ -208,10 +270,11 @@ namespace HonokaMiku
/// \param filename File name that want to be decrypted. This affects the key calculation.
/// \exception std::runtime_error The header does not match and this decrypter context can't decrypt it.
CN3_Dctx(const void* header, const char* filename);
uint32_t get_id();
/// \brief Creates SIF CN decrypter context specialized for encryption. (version 3)
/// \param filename File name that want to be encrypted. This affects the key calculation.
/// \param hdr_out Pointer with size of 16-bytes to store the file header.
static CN3_Dctx* encrypt_setup(const char* filename, void* hdr_out);
static CN3_Dctx* encrypt_setup(const char* filename, void* hdr_out, int32_t force_ver = 0);
void final_setup(const char* filename, const void* block_rest, int32_t force_ver = 0);
};

Expand All @@ -225,14 +288,15 @@ namespace HonokaMiku
/// \param header The first 4-bytes contents of the file
/// \param filename File name that want to be decrypted. This affects the key calculation.
/// \exception std::runtime_error The header does not match and this decrypter context can't decrypt it.
EN2_Dctx(const void* header, const char* filename):V2_Dctx("BFd3EnkcKa", header, filename) {}
EN2_Dctx(const void* header, const char* filename);
uint32_t get_id();
/// \brief Creates SIF EN decrypter context specialized for encryption.
/// \param filename File name that want to be encrypted. This affects the key calculation.
/// \param hdr_out Pointer with size of 16-bytes to store the file header.
inline static EN2_Dctx* encrypt_setup(const char* filename, void* hdr_out)
{
EN2_Dctx* dctx = new EN2_Dctx();
setupEncryptV2(dctx, "BFd3EnkcKa", filename, hdr_out);
setupEncryptV2(dctx, GetPrefixFromGameId(1), filename, hdr_out);
return dctx;
}
};
Expand All @@ -247,14 +311,15 @@ namespace HonokaMiku
/// \param header The first 4-bytes contents of the file
/// \param filename File name that want to be decrypted. This affects the key calculation.
/// \exception std::runtime_error The header does not match and this decrypter context can't decrypt it.
TW2_Dctx(const void* header, const char* filename):V2_Dctx("M2o2B7i3M6o6N88", header, filename) {}
TW2_Dctx(const void* header, const char* filename);
uint32_t get_id();
/// \brief Creates SIF TW decrypter context specialized for encryption.
/// \param filename File name that want to be encrypted. This affects the key calculation.
/// \param hdr_out Pointer with size of 16-bytes to store the file header.
inline static TW2_Dctx* encrypt_setup(const char* filename, void* hdr_out)
{
TW2_Dctx* dctx = new TW2_Dctx();
setupEncryptV2(dctx,"M2o2B7i3M6o6N88", filename, hdr_out);
setupEncryptV2(dctx, GetPrefixFromGameId(3), filename, hdr_out);
return dctx;
}
};
Expand All @@ -269,14 +334,15 @@ namespace HonokaMiku
/// \param header The first 4-bytes contents of the file
/// \param filename File name that want to be decrypted. This affects the key calculation.
/// \exception std::runtime_error The header does not match and this decrypter context can't decrypt it.
JP2_Dctx(const void* header, const char* filename):V2_Dctx("Hello", header, filename) {}
JP2_Dctx(const void* header, const char* filename);
uint32_t get_id();
/// \brief Creates SIF JP decrypter context specialized for encryption. (Version 2)
/// \param filename File name that want to be encrypted. This affects the key calculation.
/// \param hdr_out Pointer with size of 16-bytes to store the file header.
inline static JP2_Dctx* encrypt_setup(const char* filename, void* hdr_out)
{
JP2_Dctx* dctx = new JP2_Dctx();
setupEncryptV2(dctx, "Hello", filename, hdr_out);
setupEncryptV2(dctx, GetPrefixFromGameId(2), filename, hdr_out);
return dctx;
}
};
Expand All @@ -292,14 +358,15 @@ namespace HonokaMiku
/// \param header The first 4-bytes contents of the file
/// \param filename File name that want to be decrypted. This affects the key calculation.
/// \exception std::runtime_error The header does not match and this decrypter context can't decrypt it.
CN2_Dctx(const void* header, const char* filename):V2_Dctx("iLbs0LpvJrXm3zjdhAr4", header, filename) {}
CN2_Dctx(const void* header, const char* filename);
uint32_t get_id();
/// \brief Creates SIF CN decrypter context specialized for encryption.
/// \param filename File name that want to be encrypted. This affects the key calculation.
/// \param hdr_out Pointer with size of 16-bytes to store the file header.
inline static CN2_Dctx* encrypt_setup(const char* filename, void* hdr_out)
{
CN2_Dctx* dctx=new CN2_Dctx();
setupEncryptV2(dctx, "iLbs0LpvJrXm3zjdhAr4", filename,hdr_out);
CN2_Dctx* dctx = new CN2_Dctx();
setupEncryptV2(dctx, GetPrefixFromGameId(5), filename, hdr_out);
return dctx;
}
};
Expand All @@ -320,49 +387,39 @@ namespace HonokaMiku
/// \param header The first 4-bytes contents of the file
/// \param game_type Pointer to store the game ID. See GetPrefixFromGameId() for valid game IDs.
/// \returns DecrypterContext or NULL if no suitable decryption method is available.
DecrypterContext* FindSuitable(const char* filename, const void* header, char* game_type = NULL);
DecrypterContext* FindSuitable(const char* filename, const void* header);

/// \brief Creates decrypter context based the game ID.
/// \param game_id The game ID. Valid game IDs can be seen in HonokaMiku.cc Line 86
/// \param game_prop The game ID. Valid game IDs can be seen in HonokaMiku.cc Line 86
/// \param header The first 4-bytes contents of the file
/// \param filename File name that want to be decrypted. This affects the key calculation.
/// \returns DecrypterContext or NULL if specificed game ID is invalid
/// \exception std::runtime_error Thrown if header is not valid for decryption
DecrypterContext* CreateFrom(char game_id, const void* header, const char* filename);
DecrypterContext* RequestDecrypter(uint32_t game_prop, const void* header, const char* filename);

/// \brief Creates decrypter context for encryption based the game ID.
/// \param game_id The game ID. See GetPrefixFromGameId() for valid game IDs.
/// \param filename File name that want to be decrypted. This affects the key calculation.
/// \param header_out Pointer to store the file header. The memory size should be 16-bytes
/// to reserve space for Version 3 decrypter.
/// \returns DecrypterContext ready for encryption
DecrypterContext* EncryptPrepare(char game_id, const char* filename, void* header_out);
DecrypterContext* RequestEncrypter(uint32_t game_prop, const char* filename, void* header_out);

/// \brief Gets game key prefix for specificed game id.
/// \param game_id The game ID. Valid game IDs are
/// 1. EN/WW Version 2
/// 2. JP Version 2/KR
/// 3. TW Version 2
/// 4. JP Version 3
/// 5. CN Version 2
/// 6. EN/WW Version 3
/// 7. CN Version 3
/// 8. TW Version 3
/// \returns prefix key of specificed game ID or NULL if game ID is invalid
/// \note If you just want to get prefix key, the version doesn't matter.
inline const char* GetPrefixFromGameId(char game_id)
/// \brief Get header size for specific decryption types
/// \param dectype `HONOAMIKU_DECRYPT_*` constants
/// \returns header size (or -1 if unknown)
inline int32_t GetHeaderSize(uint32_t dectype)
{
switch(game_id)
switch(dectype & 0xFFFF0000)
{
case 1:
case 6: return "BFd3EnkcKa";
case 2:
case 4: return "Hello";
case 3:
case 8: return "M2o2B7i3M6o6N88";
case 5:
case 7: return "iLbs0LpvJrXm3zjdhAr4";
default: return NULL;
case HONOKAMIKU_DECRYPT_V1: return 0;
case HONOKAMIKU_DECRYPT_V2: return 4;
case HONOKAMIKU_DECRYPT_V3:
case HONOKAMIKU_DECRYPT_V4:
case HONOKAMIKU_DECRYPT_V5:
case HONOKAMIKU_DECRYPT_V6:
case HONOKAMIKU_DECRYPT_V7: return 16;
default: return (-1);
}
}
}
Expand Down
Loading

0 comments on commit ccaf656

Please sign in to comment.