Version 1.08 with public source code release.

This commit is contained in:
icex2
2020-10-03 20:56:55 +02:00
parent 656148121d
commit 762bf140c0
555 changed files with 55502 additions and 3 deletions
+49
View File
@@ -0,0 +1,49 @@
#include "util/adler32.h"
#include "usb-rank.h"
#define ENC_DEC_INIT_SEED 0xEBADA1
struct asset_f2_usb_rank* asset_f2_usb_rank_new(void)
{
// TODO
return malloc(sizeof(struct asset_f2_usb_rank));
}
void asset_f2_usb_rank_finalize(struct asset_f2_usb_rank* rank)
{
/* don't include the checksum field */
/*
rank->header.adler32 = util_adler32_calc(rank->header.adler_seed,
&rank->header.adler_seed,
sizeof(struct f2_profile_rank) - sizeof(uint32_t));
*/
}
char* asset_f2_usb_rank_to_string(const struct asset_f2_usb_rank* rank)
{
// TODO
return "";
}
void asset_f2_usb_rank_decrypt(uint8_t* buf, size_t len)
{
uint32_t seed = ENC_DEC_INIT_SEED;
for (int i = 0; i < len; i++){
uint8_t smbuff = buf[i];
buf[i] ^= (seed >> 8) & 0xff ;
seed = 0x68993 * (smbuff + seed) + 0x4FDCF;
}
}
void asset_f2_usb_rank_encrypt(uint8_t* buf, size_t len)
{
uint32_t seed = ENC_DEC_INIT_SEED;
for (size_t i = 0; i < len; i++){
uint8_t smbuff = buf[i] ^ ((seed >> 8) & 0xff);
buf[i] = smbuff ;
seed = 0x68993 * (smbuff + seed) + 0x4FDCF;
}
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef ASSET_F2_USB_RANK_H
#define ASSET_F2_USB_RANK_H
#include <stdint.h>
struct asset_f2_usb_rank {
// TODO
} __attribute__((__packed__));
struct asset_f2_usb_rank* asset_f2_usb_rank_new(void);
// update checksum and prepare profile to get encrypted
void asset_f2_usb_rank_finalize(struct asset_f2_usb_rank* rank);
char* asset_f2_usb_rank_to_string(const struct asset_f2_usb_rank* rank);
void asset_f2_usb_rank_decrypt(uint8_t* buf, size_t len);
void asset_f2_usb_rank_encrypt(uint8_t* buf, size_t len);
#endif
+47
View File
@@ -0,0 +1,47 @@
#include "util/adler32.h"
#include "usb-save.h"
#define ENC_DEC_INIT_SEED 0xEBADA1
struct asset_f2_usb_save* asset_f2_usb_save_new(void)
{
// TODO
return malloc(sizeof(struct asset_f2_usb_save));
}
void asset_f2_usb_save_finalize(struct asset_f2_usb_save* save)
{
/* don't include the checksum field */
save->header.adler32 = util_adler32_calc(save->header.adler_seed,
&save->header.adler_seed,
sizeof(struct asset_f2_usb_save) - sizeof(uint32_t));
}
char* asset_f2_usb_save_to_string(const struct asset_f2_usb_save* save)
{
// TODO
return "";
}
void asset_f2_usb_save_decrypt(uint8_t* buf, size_t len)
{
uint32_t seed = ENC_DEC_INIT_SEED;
for (int i = 0; i < len; i++){
uint8_t smbuff = buf[i];
buf[i] ^= (seed >> 8) & 0xff ;
seed = 0x68993 * (smbuff + seed) + 0x4FDCF;
}
}
void asset_f2_usb_save_encrypt(uint8_t* buf, size_t len)
{
uint32_t seed = ENC_DEC_INIT_SEED;
for (size_t i = 0; i < len; i++){
uint8_t smbuff = buf[i] ^ (seed >> 8) & 0xff;
buf[i] = smbuff ;
seed = 0x68993 * (smbuff + seed) + 0x4FDCF;
}
}
+105
View File
@@ -0,0 +1,105 @@
#ifndef ASSET_F2_USB_SAVE_H
#define ASSET_F2_USB_SAVE_H
#include <stdint.h>
#include <stdlib.h>
struct asset_f2_usb_save_machine_stats {
uint32_t beginner_mode_play_count;
uint32_t maniac_mode_play_count;
uint32_t unkn;
uint32_t unkn2;
uint32_t battle_mode_play_count;
uint32_t mission_zone_play_count;
uint32_t skill_zone_play_count;
uint32_t coop_mission_play_count;
} __attribute__((__packed__));
struct asset_f2_usb_save_machine_info {
/* NULL terminated */
char version[8];
/* NULL terminated */
char cpu[128];
/* NULL terminated */
char motherboard[128];
/* NULL terminated */
char gfxcard[128];
/* NULL terminated */
char hdd[32];
uint32_t total_ram_bytes;
uint32_t haspkey_id;
} __attribute__((__packed__));
struct asset_f2_usb_save_header {
uint32_t adler32;
uint32_t adler_seed;
/* NULL terminated */
char player_id[8];
uint32_t region;
uint32_t avatar_id;
uint32_t level;
float total_calories;
float total_v02;
uint64_t num_running_steps;
uint64_t num_games_played;
uint64_t exp;
uint64_t arcade_score;
uint32_t num_missions_completed;
uint32_t num_coop_missions_completed;
uint32_t battle_count_wins;
uint32_t battle_count_loses;
uint32_t battle_count_draws;
/* TODO: modifier stuff? */
uint64_t unlock;
uint32_t unlock_seed;
} __attribute__((__packed__));
struct asset_f2_usb_save_machine_highscore_entry {
uint32_t song_id;
uint8_t difficulty;
uint8_t mode;
uint8_t grade;
uint8_t clear_state;
uint32_t score_total;
uint32_t play_count;
uint32_t unkn;
/* NULL terminated */
char player_id[8];
uint32_t unkn2;
uint32_t null_padding;
} __attribute__((__packed__));
struct asset_f2_usb_save_highscore_entry {
uint32_t song_id;
uint8_t difficulty;
uint8_t mode;
uint8_t grade;
uint8_t clear_and_unlock_state;
uint32_t score_total;
uint32_t play_count;
uint32_t null_padding;
} __attribute__((__packed__));
struct asset_f2_usb_save {
/* start offset: 0x00 */
struct asset_f2_usb_save_header header;
/* start offset: 0x120 */
struct asset_f2_usb_save_machine_info machine_info;
struct asset_f2_usb_save_machine_stats machine_stats;
/* start offset: 0x2F0 */
// TODO continue
} __attribute__((__packed__));
struct asset_f2_usb_save* asset_f2_usb_save_new(void);
// update checksum and prepare profile to get encrypted
void asset_f2_usb_save_finalize(struct asset_f2_usb_save* save);
char* asset_f2_usb_save_to_string(const struct asset_f2_usb_save* save);
void asset_f2_usb_save_decrypt(uint8_t* buf, size_t len);
void asset_f2_usb_save_encrypt(uint8_t* buf, size_t len);
#endif
+96
View File
@@ -0,0 +1,96 @@
#define LOG_MODULE "asset-f2-util"
#include <string.h>
#include "util/fs.h"
#include "util/log.h"
#include "util.h"
struct asset_f2_usb_rank* asset_f2_util_rank_usb_load_from_file(const char* path, bool encrypted)
{
struct asset_f2_usb_rank* rank;
size_t size;
if (!util_file_load(path, (void**) &rank, &size, false)) {
log_error("Loading f2 rank file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_f2_usb_rank)) {
log_error("Invalid size of f2 rank file %s: %d != %d", path, size,
sizeof(struct asset_f2_usb_rank));
free(rank);
return NULL;
}
if (encrypted) {
asset_f2_usb_rank_decrypt((uint8_t*) rank, size);
}
return rank;
}
bool asset_f2_util_rank_usb_save_to_file(const char* path, const struct asset_f2_usb_rank* rank, bool encrypt)
{
struct asset_f2_usb_rank rank_enc;
memcpy(&rank_enc, rank, sizeof(struct asset_f2_usb_rank));
if (encrypt) {
asset_f2_usb_rank_encrypt((uint8_t*) &rank_enc,
sizeof(struct asset_f2_usb_rank));
}
if (!util_file_save(path, (void**) &rank_enc,
sizeof(struct asset_f2_usb_rank))) {
log_error("Storing f2 rank data to %s failed", path);
return false;
}
return true;
}
struct asset_f2_usb_save* asset_f2_util_usb_save_load_from_file(const char* path, bool encrypted)
{
struct asset_f2_usb_save* save;
size_t size;
if (!util_file_load(path, (void**) &save, &size, false)) {
log_error("Loading f2 save file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_f2_usb_save)) {
log_error("Invalid size of f2 save file %s: %d != %d", path, size,
sizeof(struct asset_f2_usb_save));
free(save);
return NULL;
}
if (encrypted) {
asset_f2_usb_save_decrypt((uint8_t*) save, size);
}
return save;
}
bool asset_f2_util_usb_save_save_to_file(const char* path, const struct asset_f2_usb_save* save, bool encrypt)
{
struct asset_f2_usb_save save_enc;
memcpy(&save_enc, save, sizeof(struct asset_f2_usb_save));
if (encrypt) {
asset_f2_usb_save_encrypt((uint8_t*) &save_enc,
sizeof(struct asset_f2_usb_save));
}
if (!util_file_save(path, (const void*) &save_enc,
sizeof(struct asset_f2_usb_save))) {
log_error("Storing f2 save data to %s failed", path);
return false;
}
return true;
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef ASSET_F2_UTIL_H
#define ASSET_F2_UTIL_H
#include <stdbool.h>
#include "usb-rank.h"
#include "usb-save.h"
// load from file and decrypt, returns decrypted struct
struct asset_f2_usb_rank* asset_f2_util_usb_rank_load_from_file(const char* path, bool encrypted);
// takes decrypted rank data, encrypts it and writes it to file
bool asset_f2_util_usb_rank_save_to_file(const char* path, const struct asset_f2_usb_rank* rank, bool encrypt);
// load from file and decrypt, returns decrypted struct
struct asset_f2_usb_save* asset_f2_util_usb_save_load_from_file(const char* path, bool encrypted);
// takes decrypted save data, encrypts it and writes it to file
bool asset_f2_util_usb_save_save_to_file(const char* path, const struct asset_f2_usb_save* save, bool encrypt);
#endif
+32
View File
@@ -0,0 +1,32 @@
#define LOG_MODULE "fex-profile-rank"
#include "usb-rank.h"
struct asset_fex_usb_rank* asset_fex_usb_rank_new(void)
{
return malloc(sizeof(struct asset_fex_usb_rank));
}
void asset_fex_usb_rank_finalize(struct asset_fex_usb_rank* rank)
{
}
char* asset_fex_usb_rank_to_string(const struct asset_fex_usb_rank* rank)
{
return malloc(0);
}
void asset_fex_usb_rank_decrypt(uint8_t* buf, size_t len)
{
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_fex_usb_rank_encrypt(uint8_t* buf, size_t len)
{
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef ASSET_FEX_USB_RANK_H
#define ASSET_FEX_USB_RANK_H
#include <stdint.h>
#include <stdlib.h>
struct asset_fex_usb_rank_header {
uint32_t adler32;
/* NULL terminated */
char player_id[8];
} __attribute__((__packed__));
struct asset_fex_usb_rank {
struct asset_fex_usb_rank_header header;
} __attribute__((__packed__));
struct asset_fex_usb_rank* asset_fex_usb_rank_new(void);
// update checksum and prepare profile to get encrypted
void asset_fex_usb_rank_finalize(struct asset_fex_usb_rank* rank);
char* asset_fex_usb_rank_to_string(const struct asset_fex_usb_rank* rank);
void asset_fex_usb_rank_decrypt(uint8_t* buf, size_t len);
void asset_fex_usb_rank_encrypt(uint8_t* buf, size_t len);
#endif
+339
View File
@@ -0,0 +1,339 @@
#include <stdio.h>
#include <string.h>
#include "util/adler32.h"
#include "util/mem.h"
#include "util/str.h"
#include "usb-save.h"
static char* fex_profile_save_header_to_string(
const struct asset_fex_usb_save_header* header)
{
char* buffer;
buffer = (char*) util_xmalloc(256);
memset(buffer, 0, 256);
util_str_format(buffer, 256,
"-------------- header --------------\n"
"adler32: %X\n"
"usb_serial: %s\n"
"dongle_serial: %X\n",
header->adler32,
header->usb_serial,
header->dongle_serial);
return buffer;
}
static char* fex_profile_save_player_to_string(
const struct asset_fex_usb_save_player* player)
{
char* buffer;
buffer = (char*) util_xmalloc(1024);
memset(buffer, 0, 1024);
util_str_format(buffer, 1024,
"-------------- player --------------\n"
"unkn: %X\n"
"unkn2: %X\n"
"avatar_id: %d\n"
"player_lvl: %d\n"
"unkn3: %X\n"
"player_id: %s\n"
"unkn4: %X\n"
"unkn5: %X\n"
"unkn6: %X\n"
"unkn7: %X\n"
"unkn8: %X\n"
"unkn9: %X\n"
"unkn10: %X\n"
"unkn11: %X\n"
"unkn12: %X\n"
"unkn13: %X\n"
"unkn14: %X\n"
"unkn15: %X\n"
"total_step: %d\n"
"play_count: %d\n"
"exp: %d\n"
"num_quest_world_passed: %d\n"
"unkn16: %X\n"
"arcade_score: %d\n"
"num_battle_wins: %d\n"
"num_battle_loss: %d\n"
"num_battle_draw: %d\n",
player->unkn,
player->unkn2,
player->avatar_id,
player->player_lvl,
player->unkn3,
player->player_id,
player->unkn4,
player->unkn5,
player->unkn6,
player->unkn7,
player->unkn8,
player->unkn9,
player->unkn10,
player->unkn11,
player->unkn12,
player->unkn13,
player->unkn14,
player->unkn15,
player->total_step,
player->play_count,
player->exp,
player->num_quest_world_passed,
player->unkn16,
player->arcade_score,
player->num_battle_wins,
player->num_battle_loss,
player->num_battle_draw);
return buffer;
}
static char* fex_profile_save_mods_to_string(
const struct asset_fex_usb_save_player_mods* mods)
{
char* buffer;
buffer = (char*) util_xmalloc(128);
memset(buffer, 0, 128);
util_str_format(buffer, 128,
"-------------- modifiers --------------\n"
"modifiers: %X\n"
"half_speed_mod: %X\n"
"noteskin: %X\n",
mods->modifiers,
mods->half_speed_mod,
mods->noteskin);
return buffer;
}
static char* fex_profile_save_mod_unlocks_to_string(
const struct asset_fex_usb_save_mod_unlocks* unlocks)
{
char* merged;
char* buffer;
char* tmp;
const size_t buf_size = 512;
merged = (char*) util_xmalloc(buf_size);
memset(merged, 0, buf_size);
buffer = (char*) util_xmalloc(buf_size);
memset(buffer, 0, buf_size);
util_str_format(buffer, buf_size,
"-------------- mod unlocks --------------\n" );
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
for (uint32_t i = 0; i < 0x12; i++) {
util_str_format(buffer, buf_size,
"unkn0[%d]: %d\n",
i,
unlocks->unkn0[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
util_str_format(buffer, buf_size,
"display_va: %d\n",
unlocks->display_va);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
for (uint32_t i = 0; i < 0x0D; i++) {
util_str_format(buffer, buf_size,
"unkn1[%d]: %d\n",
i,
unlocks->unkn1[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x10; i++) {
util_str_format(buffer, buf_size,
"noteskins[%d]: %d\n",
i,
unlocks->noteskins[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x04; i++) {
util_str_format(buffer, buf_size,
"unkn2[%d]: %d\n",
i,
unlocks->unkn2[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x03; i++) {
util_str_format(buffer, buf_size,
"path[%d]: %d\n",
i,
unlocks->path[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x06; i++) {
util_str_format(buffer, buf_size,
"unkn3[%d]: %d\n",
i,
unlocks->unkn3[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
util_str_format(buffer, buf_size,
"xj: %d\n",
unlocks->xj);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
for (uint32_t i = 0; i < 0x02; i++) {
util_str_format(buffer, buf_size,
"system[%d]: %d\n",
i,
unlocks->system[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x08; i++) {
util_str_format(buffer, buf_size,
"rush[%d]: %d\n",
i,
unlocks->rush[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x1A; i++) {
util_str_format(buffer, buf_size,
"unkn4[%d]: %d\n",
i,
unlocks->unkn4[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
return merged;
}
static char* fex_profile_save_mission_state_to_string(
const struct asset_fex_usb_save_mission_state* state)
{
char* buffer;
buffer = (char*) util_xmalloc(128);
memset(buffer, 0, 128);
util_str_format(buffer, 128,
"-------------- mission state --------------\n"
"quest_world_location: %X\n"
"skill_up_zone_location: %X\n",
state->quest_world_location,
state->skill_up_zone_location);
return buffer;
}
struct asset_fex_usb_save* asset_fex_usb_save_new(void)
{
return malloc(sizeof(struct asset_fex_usb_save));
}
void asset_fex_usb_save_finalize(struct asset_fex_usb_save* save)
{
}
char* asset_fex_usb_save_to_string(const struct asset_fex_usb_save* save)
{
char* header;
char* player;
char* mods;
char* unlocks;
char* mission_state;
char* merged;
char* merged2;
header = fex_profile_save_header_to_string(&save->header);
player = fex_profile_save_player_to_string(&save->player);
mods = fex_profile_save_mods_to_string(&save->mods);
unlocks = fex_profile_save_mod_unlocks_to_string(&save->mod_unlocks);
mission_state = fex_profile_save_mission_state_to_string(&save->mission_state);
merged = util_str_merge(header, player);
merged2 = util_str_merge(merged, mods);
free(merged);
merged = util_str_merge(merged2, unlocks);
free(merged2);
merged2 = util_str_merge(merged, mission_state);
free(merged);
free(header);
free(player);
free(mods);
free(unlocks);
free(mission_state);
return merged2;
}
void asset_fex_usb_save_decrypt(uint8_t* buf, size_t len)
{
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_fex_usb_save_encrypt(uint8_t* buf, size_t len)
{
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+191
View File
@@ -0,0 +1,191 @@
#ifndef ASSET_FEX_USB_SAVE_H
#define ASSET_FEX_USB_SAVE_H
#include <stdint.h>
#include <stdlib.h>
enum asset_fex_usb_save_noteskins {
ASSET_FEX_USB_SAVE_SKIN_DEFAULT = 0x00,
ASSET_FEX_USB_SAVE_SKIN_HANAFUDA = 0x01,
ASSET_FEX_USB_SAVE_SKIN_CLASSIC = 0x02,
ASSET_FEX_USB_SAVE_SKIN_CANDY = 0x03,
ASSET_FEX_USB_SAVE_SKIN_PORING = 0x04,
ASSET_FEX_USB_SAVE_SKIN_MUSICAL_NOTES = 0x05,
ASSET_FEX_USB_SAVE_SKIN_CANON_D = 0x06,
ASSET_FEX_USB_SAVE_SKIN_PLAYING_CARDS = 0x07,
ASSET_FEX_USB_SAVE_SKIN_NX = 0x08,
ASSET_FEX_USB_SAVE_SKIN_DIGNITY = 0x09,
ASSET_FEX_USB_SAVE_SKIN_HORSE_HEAD = 0x0A,
ASSET_FEX_USB_SAVE_SKIN_DOG = 0x0B,
ASSET_FEX_USB_SAVE_SKIN_GIRL = 0x0C,
ASSET_FEX_USB_SAVE_SKIN_FIRE = 0x0D,
ASSET_FEX_USB_SAVE_SKIN_ICE = 0x0E,
ASSET_FEX_USB_SAVE_SKIN_WIND = 0x0F,
ASSET_FEX_USB_SAVE_SKIN_RED_DP = 0x10,
ASSET_FEX_USB_SAVE_SKIN_BLUE_DP = 0x11,
ASSET_FEX_USB_SAVE_SKIN_YELLOW_DP = 0x12,
ASSET_FEX_USB_SAVE_SKIN_NXA = 0x13,
ASSET_FEX_USB_SAVE_SKIN_NX2 = 0x14,
ASSET_FEX_USB_SAVE_SKIN_ELECTRIC_FIESTA = 0x15,
ASSET_FEX_USB_SAVE_SKIN_DRUM = 0x16,
ASSET_FEX_USB_SAVE_SKIN_BULLET = 0x17,
ASSET_FEX_USB_SAVE_SKIN_BLUE_DRUM = 0x18,
ASSET_FEX_USB_SAVE_SKIN_RED_DRUM = 0x19,
ASSET_FEX_USB_SAVE_SKIN_YELLOW_DRUM = 0x1A,
ASSET_FEX_USB_SAVE_SKIN_WORLD_CUP = 0x1B,
ASSET_FEX_USB_SAVE_SKIN_PREX3 = 0x1C,
ASSET_FEX_USB_SAVE_SKIN_FIESTA_BASIC = 0x1D,
};
struct asset_fex_usb_save_header {
/* 0x00 */
uint32_t adler32;
/* 0x04: NULL terminated */
char usb_serial[0x40];
/* 0x44: Deprecated from old games */
uint32_t dongle_serial;
} __attribute__((__packed__));
struct asset_fex_usb_save_player {
/* 0x48 */
uint32_t unkn;
/* 0x4C */
uint32_t unkn2;
/* 0x50 */
uint8_t avatar_id;
/* 0x51: Range: 0x00 - 0x21 */
uint8_t player_lvl;
/* 0x52: PumBi sets it to 0xC4 */
uint16_t unkn3;
/* 0x54: NULL terminated */
char player_id[8];
/* 0x5C */
uint32_t unkn4;
/* 0x60 */
uint32_t unkn5;
/* 0x64: Each time you save, it inc. by 6... */
uint32_t unkn6;
/* 0x68 */
uint32_t unkn7;
/* 0x6C */
uint32_t unkn8;
/* 0x70 */
uint32_t unkn9;
/* 0x74 */
uint32_t unkn10;
/* 0x78 */
uint32_t unkn11;
/* 0x7C */
uint32_t unkn12;
/* 0x80 */
uint32_t unkn13;
/* 0x84 */
uint32_t unkn14;
/* 0x88 */
uint32_t unkn15;
/* 0x8C */
uint32_t total_step;
/* 0x90 */
uint32_t play_count;
/* 0x94 */
uint32_t exp;
/* 0x98 */
uint32_t num_quest_world_passed;
/* 0x9C */
uint32_t unkn16;
/* 0xA0 */
uint32_t arcade_score;
/* 0xA8 */
uint32_t num_battle_wins;
/* 0xAC */
uint32_t num_battle_loss;
/* 0xB0 */
uint32_t num_battle_draw;
} __attribute__((__packed__));
struct asset_fex_usb_save_player_mods {
/* 0xB4: The first set of saved modifiers (see MOD_1 below) */
uint32_t modifiers;
/* 0xB8: If the +0.5x mod is enabled (boolean) */
uint32_t half_speed_mod;
/* 0xBC: Which noteskin is currently selected (see NOTE_S below) */
uint32_t noteskin;
} __attribute__((__packed__));
struct asset_fex_usb_save_mod_unlocks {
/* 0xC0: Unknown Unlocks Area */
uint8_t unkn0[0x12];
/* 0xD2: Boolean for unlocking VA Mod. */
uint8_t display_va;
/* 0xD3: Unknown Unlocks Area */
uint8_t unkn1[0x0D];
/* 0xE0: Array of bools - Which Noteskins are Unlocked. */
uint8_t noteskins[0x10];
/* 0xF0: Unknown Unlocks Area */
uint8_t unkn2[0x04];
/* 0xF4: Array of bools - Which Path Mods are Unlocked. */
uint8_t path[0x03];
/* 0xF7: Unknown Unlocks Area */
uint8_t unkn3[0x06];
/* 0xFE: Boolean for unlocking XJ */
uint8_t xj;
/* 0x100: Array of bools - Which System Mods are Unlocked. */
uint8_t system[0x02];
/* 0x102: Array of bools - Which Rush/NOT Modes are Unlocked. */
uint8_t rush[0x08];
/* 0x10A: Unknown Unlocks Area */
uint8_t unkn4[0x1A];
} __attribute__((__packed__));
struct asset_fex_usb_save_mission_state {
/* 0x124 */
uint32_t quest_world_location;
/* 0x128 */
uint32_t skill_up_zone_location;
} __attribute__((__packed__));
struct asset_fex_usb_save {
struct asset_fex_usb_save_header header;
struct asset_fex_usb_save_player player;
struct asset_fex_usb_save_player_mods mods;
struct asset_fex_usb_save_mod_unlocks mod_unlocks;
struct asset_fex_usb_save_mission_state mission_state;
/* Not verified, yet. just notes from old tools */
/* 0x17AD (0x0A): Quest world inventory */
/* 0x17B7: ??? */
/* 0x17B8 (0x1C): Quest world warp unlocks */
/* 0x17AC (0x11): QW inventory */
/* 0x17D4 or 0x17D5 ? (0x14): favorites channel */
/* 0x2E48 - 0x1A5EF: step chart blacklist */
/* 0x2E48 - 0x1A5EF: step chart unlocks = blacklist */
/* pumbi stepcharts */
/* 0x6047: pumbi stepchart 1009 Level S15 */
/* 0x6083 */
/* 0x60AB */
/* 0x647F */
/* 0x64BB */
/* 0x64CF */
/* 0x650B */
/* 0x6533 */
/* 0x655B */
/* 0x65AB */
/* 0x660F */
} __attribute__((__packed__));
struct asset_fex_usb_save* asset_fex_usb_save_new(void);
// update checksum and prepare profile to get encrypted
void asset_fex_usb_save_finalize(struct asset_fex_usb_save* save);
char* asset_fex_usb_save_to_string(const struct asset_fex_usb_save* save);
void asset_fex_usb_save_decrypt(uint8_t* buf, size_t len);
void asset_fex_usb_save_encrypt(uint8_t* buf, size_t len);
#endif
+100
View File
@@ -0,0 +1,100 @@
#define LOG_MODULE "asset-fex-util"
#include <string.h>
#include "util/fs.h"
#include "util/log.h"
#include "util.h"
struct asset_fex_usb_rank* asset_fex_usb_rank_load_from_file(const char* path,
bool encrypted)
{
struct asset_fex_usb_rank* rank;
size_t size;
if (!util_file_load(path, (void**) &rank, &size, false)) {
log_error("Loading fex rank file %s failed", path);
return NULL;
}
// if (size != sizeof(struct fex_profile_rank)) {
// log_error("Invalid size of fex rank file %s: %d != %d", path, size,
// sizeof(struct fex_profile_rank));
// free(rank);
// return NULL;
// }
if (encrypted) {
asset_fex_usb_rank_decrypt((uint8_t*) rank, size);
}
return rank;
}
bool asset_fex_usb_rank_save_to_file(const char* path,
const struct asset_fex_usb_rank* rank, bool encrypt)
{
struct asset_fex_usb_rank rank_enc;
memcpy(&rank_enc, rank, sizeof(struct asset_fex_usb_rank));
if (encrypt) {
asset_fex_usb_rank_encrypt((uint8_t*) &rank_enc,
sizeof(struct asset_fex_usb_rank));
}
if (!util_file_save(path, (void**) &rank_enc,
sizeof(struct asset_fex_usb_rank))) {
log_error("Storing fex rank data to %s failed", path);
return false;
}
return true;
}
struct asset_fex_usb_save* asset_fex_usb_save_load_from_file(const char* path,
bool encrypted)
{
struct asset_fex_usb_save* save;
size_t size;
if (!util_file_load(path, (void**) &save, &size, false)) {
log_error("Loading fex save file %s failed", path);
return NULL;
}
// if (size != sizeof(struct fex_profile_save)) {
// log_error("Invalid size of fex save file %s: %d != %d", path, size,
// sizeof(struct fex_profile_save));
// free(save);
// return NULL;
// }
if (encrypted) {
asset_fex_usb_save_decrypt((uint8_t*) save, size);
}
return save;
}
bool asset_fex_usb_save_save_to_file(const char* path,
const struct asset_fex_usb_save* save, bool encrypt)
{
struct asset_fex_usb_save save_enc;
memcpy(&save_enc, save, sizeof(struct asset_fex_usb_save));
if (encrypt) {
asset_fex_usb_save_encrypt((uint8_t*) &save_enc,
sizeof(struct asset_fex_usb_save));
}
if (!util_file_save(path, (const void*) &save_enc,
sizeof(struct asset_fex_usb_save))) {
log_error("Storing fex save data to %s failed", path);
return false;
}
return true;
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef ASSET_FEX_UTIL_H
#define ASSET_FEX_UTIL_H
#include <stdbool.h>
#include "usb-rank.h"
#include "usb-save.h"
// load from file and decrypt, returns decrypted struct
struct asset_fex_usb_rank* asset_fex_usb_rank_load_from_file(const char* path, bool encrypted);
// takes decrypted rank data, encrypts it and writes it to file
bool asset_fex_usb_rank_save_to_file(const char* path, const struct asset_fex_usb_rank* rank, bool encrypt);
// load from file and decrypt, returns decrypted struct
struct asset_fex_usb_save* asset_fex_usb_save_load_from_file(const char* path, bool encrypted);
// takes decrypted save data, encrypts it and writes it to file
bool asset_fex_usb_save_save_to_file(const char* path, const struct asset_fex_usb_save* save, bool encrypt);
#endif
+154
View File
@@ -0,0 +1,154 @@
/**
* Tool for FiestaEX profiles: decrypt, encrypt, print profile data
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asset/fex/lib/util.h"
#include "util/str.h"
int main(int argc, char** argv)
{
int ret;
char* rank_path;
char* save_path;
if (argc < 3) {
printf("Usage: %s [cmd: new, dec, enc, dump] "
"[path containing fiestaex_rank.bin fiestaex_save.bin]\n", argv[0]);
return -1;
}
ret = 0;
rank_path = util_str_merge(argv[2], "fiestaex_rank.bin");
save_path = util_str_merge(argv[2], "fiestaex_save.bin");
if (!strcmp(argv[1], "new")) {
struct asset_fex_usb_rank* rank = asset_fex_usb_rank_new();
struct asset_fex_usb_save* save = asset_fex_usb_save_new();
asset_fex_usb_rank_finalize(rank);
asset_fex_usb_save_finalize(save);
if (!asset_fex_usb_rank_save_to_file(rank_path, rank, true)) {
fprintf(stderr, "Creating rank file %s failed\n", rank_path);
ret = -2;
}
if (!asset_fex_usb_save_save_to_file(save_path, save, true)) {
fprintf(stderr, "Creating save file %s failed\n", save_path);
ret = -3;
}
free(rank);
free(save);
} else {
struct asset_fex_usb_rank* rank;
struct asset_fex_usb_save* save;
bool encrypted;
if (!strcmp(argv[1], "enc")) {
char* tmp;
encrypted = false;
tmp = util_str_merge(rank_path, ".dec");
free(rank_path);
rank_path = tmp;
tmp = util_str_merge(save_path, ".dec");
free(save_path);
save_path = tmp;
} else {
encrypted = true;
}
rank = asset_fex_usb_rank_load_from_file(rank_path, encrypted);
save = asset_fex_usb_save_load_from_file(save_path, encrypted);
if (!rank) {
fprintf(stderr, "Loading %s failed", rank_path);
ret = -4;
goto cleanup;
}
if (!save) {
fprintf(stderr, "Loading %s failed", save_path);
ret = -5;
free(rank);
goto cleanup;
}
if (!strcmp(argv[1], "dec")) {
char* rank_path_dec;
char* save_path_dec;
rank_path_dec = util_str_merge(rank_path, ".dec");
save_path_dec = util_str_merge(save_path, ".dec");
if (!asset_fex_usb_rank_save_to_file(rank_path_dec, rank, false)) {
fprintf(stderr, "Saving decrypted rank file to %s failed\n",
rank_path_dec);
ret = -6;
}
if (!asset_fex_usb_save_save_to_file(save_path_dec, save, false)) {
fprintf(stderr, "Saving decrypted save file to %s failed\n",
save_path_dec);
ret = -7;
}
free(rank_path_dec);
free(save_path_dec);
} else if (!strcmp(argv[1], "enc")) {
char* rank_path_enc;
char* save_path_enc;
rank_path_enc = util_str_merge(rank_path, ".enc");
save_path_enc = util_str_merge(save_path, ".enc");
if (!asset_fex_usb_rank_save_to_file(rank_path_enc, rank, true)) {
fprintf(stderr, "Saving encrypted rank file to %s failed\n",
rank_path_enc);
ret = -6;
}
if (!asset_fex_usb_save_save_to_file(save_path_enc, save, true)) {
fprintf(stderr, "Saving encrypted save file to %s failed\n",
save_path_enc);
ret = -7;
}
free(rank_path_enc);
free(save_path_enc);
} else if (!strcmp(argv[1], "dump")) {
char* rank_str;
char* save_str;
rank_str = asset_fex_usb_rank_to_string(rank);
save_str = asset_fex_usb_save_to_string(save);
printf("----------------- rank -----------------\n"
"%s----------------- save -----------------\n%s",
rank_str, save_str);
free(rank_str);
free(save_str);
} else {
fprintf(stderr, "Unknown command %s\n", argv[1]);
ret = -4;
}
free(rank);
free(save);
}
cleanup:
free(rank_path);
free(save_path);
return ret;
}
+38
View File
@@ -0,0 +1,38 @@
#ifndef ASSET_GAME_VERSION_H
#define ASSET_GAME_VERSION_H
/* Note: Version IDs must be sync with server code */
enum asset_game_version {
ASSET_GAME_VERSION_INVALID = 0,
ASSET_GAME_VERSION_FIRST = 1,
ASSET_GAME_VERSION_SECOND = 2,
ASSET_GAME_VERSION_OBG = 3,
ASSET_GAME_VERSION_OBG_SE = 4,
ASSET_GAME_VERSION_THE_COLLECTION = 5,
ASSET_GAME_VERSION_PERFECT_COLLECTION = 6,
ASSET_GAME_VERSION_EXTRA = 7,
ASSET_GAME_VERSION_PREMIERE_1 = 8,
ASSET_GAME_VERSION_PREX_1 = 9,
ASSET_GAME_VERSION_REBIRTH = 10,
ASSET_GAME_VERSION_PREMIERE_2 = 11,
ASSET_GAME_VERSION_PREX_2 = 12,
ASSET_GAME_VERSION_PREMIERE_3 = 13,
ASSET_GAME_VERSION_PREX_3 = 14,
ASSET_GAME_VERSION_EXCEED = 15,
ASSET_GAME_VERSION_EXCEED_2 = 16,
ASSET_GAME_VERSION_ZERO = 17,
ASSET_GAME_VERSION_NX = 18,
ASSET_GAME_VERSION_PRO = 19,
ASSET_GAME_VERSION_NX2 = 20,
ASSET_GAME_VERSION_NXA = 21,
ASSET_GAME_VERSION_FIESTA = 22,
ASSET_GAME_VERSION_PRO2 = 23,
ASSET_GAME_VERSION_FIESTA_EX = 24,
ASSET_GAME_VERSION_FIESTA_2 = 25,
ASSET_GAME_VERSION_INFINITY = 26,
ASSET_GAME_VERSION_PRIME = 27,
ASSET_GAME_VERSION_PRIME_2 = 28,
ASSET_GAME_VERSION_XX = 29
};
#endif
+68
View File
@@ -0,0 +1,68 @@
#include <string.h>
#include "asset/nx/lib/settings.h"
#include "util/adler32.h"
#include "util/mem.h"
struct asset_nx_settings* asset_nx_settings_new(void)
{
struct asset_nx_settings* settings;
settings = util_xmalloc(sizeof(struct asset_nx_settings));
memset(settings->ff_pad, 0xFF, sizeof(settings->ff_pad));
memcpy(settings->ident, "NX10", 4);
/* Updated using finalize */
settings->adler32 = 0;
settings->total_uptime = 0;
settings->language = ASSET_NX_SETTINGS_LANG_ENG;
memset(settings->song_play_count, 0, sizeof(uint32_t) * ASSET_NX_SETTINGS_MAX_SONGS);
settings->game_option_default_station = ASSET_NX_SETTINGS_STATION_TRAIN;
settings->game_mode = ASSET_NX_SETTINGS_GAME_MODE_NORMAL;
settings->game_level = ASSET_NX_SETTINGS_LEVEL_NORMAL;
settings->game_stage_break = ASSET_NX_SETTINGS_STAGE_BREAK_2ND;
settings->game_demo_sound = 1;
settings->game_show_help = 1;
settings->game_mercy_ticket = 0;
settings->game_score_per_ticket = 0;
settings->coin_limit = 0;
settings->coin1_setting = 1;
settings->coin2_setting = 1;
memset(settings->unknown_unlock_flags, 0, ASSET_NX_SETTINGS_NUM_UNLOCK_FLAGS);
settings->unkn2 = 0x2A;
settings->unkn3 = 0;
settings->bookkeeping_coin1_total = 0;
settings->bookkeeping_coin2_total = 0;
settings->bookkeeping_service_total = 0;
settings->bookkeeping_play_total = 0;
settings->bookkeeping_faep21_play_count = 0;
settings->bookkeeping_ticket_1 = 0;
settings->bookkeeping_ticket_2 = 0;
settings->region = 0xFF;
memset(settings->censor_table, 0, ASSET_NX_SETTINGS_MAX_SONGS);
return settings;
}
void asset_nx_settings_finalize(struct asset_nx_settings* settings)
{
/* Seems odd, but they just checksum the game settings part...*/
settings->adler32 = util_adler32_calc(1, (const uint8_t*) &settings->game_mode, 8);
}
void asset_nx_settings_unlock_all(struct asset_nx_settings* settings)
{
/* FAEP 2-1 must be played at least 100 times to unlock FAEP 2-2 */
settings->bookkeeping_faep21_play_count = 100;
/* To unlock all world tour missions and special zone songs, set this to 20k.
The first 10k unlock only the first half of the missions while another 10k unlocks the second half (wtf) */
settings->bookkeeping_play_total = 20000;
}
char* asset_nx_settings_to_string(const struct asset_nx_settings* settings)
{
// TODO
return "";
}
+101
View File
@@ -0,0 +1,101 @@
#ifndef ASSET_NX_SETTINGS_H
#define ASSET_NX_SETTINGS_H
#include <stdint.h>
#define ASSET_NX_SETTINGS_FILE_NAME "PIUNX.INI"
#define ASSET_NX_SETTINGS_MAX_SONGS 200
#define ASSET_NX_SETTINGS_NUM_UNLOCK_FLAGS 59
enum asset_nx_settings_language {
ASSET_NX_SETTINGS_LANG_KOR = 0,
ASSET_NX_SETTINGS_LANG_ENG = 1,
ASSET_NX_SETTINGS_LANG_SP = 2,
ASSET_NX_SETTINGS_LANG_CH = 3,
};
enum asset_nx_settings_station {
ASSET_NX_SETTINGS_STATION_TRAIN = 0,
ASSET_NX_SETTINGS_STATION_ARCADE = 1,
ASSET_NX_SETTINGS_STATION_WORLD = 2,
ASSET_NX_SETTINGS_STATION_SPECIAL = 3,
};
enum asset_nx_settings_game_mode {
ASSET_NX_SETTINGS_GAME_MODE_NORMAL = 0,
ASSET_NX_SETTINGS_GAME_MODE_EVENT = 1,
};
enum asset_nx_settings_level {
ASSET_NX_SETTINGS_LEVEL_EASY = 0,
ASSET_NX_SETTINGS_LEVEL_NORMAL = 1,
ASSET_NX_SETTINGS_LEVEL_HARD = 2,
};
enum asset_nx_settings_stage_break {
ASSET_NX_SETTINGS_STAGE_BREAK_OFF = 0,
ASSET_NX_SETTINGS_STAGE_BREAK_1ST = 1,
ASSET_NX_SETTINGS_STAGE_BREAK_2ND = 2,
ASSET_NX_SETTINGS_STAGE_BREAK_3RD = 3,
ASSET_NX_SETTINGS_STAGE_BREAK_4TH = 4,
};
struct asset_nx_settings {
/* Padding to fill up to 4k EEPROM legacy size */
uint8_t ff_pad[0xB9D];
/* NX10 */
char ident[4];
uint32_t adler32;
uint32_t total_uptime;
uint8_t language;
uint32_t song_play_count[ASSET_NX_SETTINGS_MAX_SONGS];
uint32_t game_option_default_station;
uint8_t game_mode;
uint8_t game_level;
uint8_t game_stage_break;
/* 0 off, 1 on */
uint8_t game_demo_sound;
/* 0 off, 1 on */
uint8_t game_show_help;
/* 0 off, 1 on */
uint8_t game_mercy_ticket;
/* 0 off, 1 - 6 for score */
uint8_t game_score_per_ticket;
/* 0 none */
uint8_t coin_limit; // 0 none
/* 0 freeplay */
uint8_t coin1_setting;
/* 0 freeplay */
uint8_t coin2_setting;
/* Unlock flags for world tour missions? setting them to 1 doesn't unlock anything.
64 missions total, 55 unlock flags, 0 = locked, 1 = unlocked */
uint8_t unknown_unlock_flags[ASSET_NX_SETTINGS_NUM_UNLOCK_FLAGS];
/* Default value 0x2A */
uint32_t unkn2;
uint32_t unkn3;
uint32_t bookkeeping_coin1_total;
uint32_t bookkeeping_coin2_total;
uint32_t bookkeeping_service_total;
uint32_t bookkeeping_play_total;
/* Separate counter for FAEP 2-1 play counts. Once this hits 100, FAEP 2-2 is unlocked in arcade station */
uint32_t bookkeeping_faep21_play_count;
uint32_t bookkeeping_ticket_1;
uint32_t bookkeeping_ticket_2;
/* FF -> not used, censoring globally disabled. unused feature though */
uint8_t region;
/* 0 = censoring of song off */
uint8_t censor_table[ASSET_NX_SETTINGS_MAX_SONGS];
} __attribute__((__packed__));
struct asset_nx_settings* asset_nx_settings_new(void);
// update checksum
void asset_nx_settings_finalize(struct asset_nx_settings* settings);
// unlocks FAEP 2-2 by setting the play count to 100
// and sets all missions cleared to unlock special stage songs and other missions
void asset_nx_settings_unlock_all(struct asset_nx_settings* settings);
char* asset_nx_settings_to_string(const struct asset_nx_settings* settings);
#endif
+35
View File
@@ -0,0 +1,35 @@
#include <string.h>
#include "asset/nx/lib/util.h"
#include "util/fs.h"
#include "util/log.h"
struct asset_nx_settings* asset_nx_util_load_settings_from_file(const char* path)
{
struct asset_nx_settings* settings;
size_t size;
if (!util_file_load(path, (void**) &settings, &size, false)) {
log_error("Loading nx setting file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_nx_settings)) {
log_error("Invalid size of nx setting file %s: %d != %d", path, size, sizeof(struct asset_nx_settings));
free(settings);
return NULL;
}
return settings;
}
bool asset_nx_util_save_settings_to_file(const char* path, const struct asset_nx_settings* settings)
{
if (!util_file_save(path, (void**) settings, sizeof(struct asset_nx_settings))) {
log_error("Storing nx settings data to %s failed", path);
return false;
}
return true;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef ASSET_NX_UTIL_H
#define ASSET_NX_UTIL_H
#include <stdbool.h>
#include "asset/nx/lib/settings.h"
struct asset_nx_settings* asset_nx_util_load_settings_from_file(const char* path);
bool asset_nx_util_save_settings_to_file(const char* path, const struct asset_nx_settings* settings);
#endif
+88
View File
@@ -0,0 +1,88 @@
/**
* Tool for NX settings files
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asset/nx/lib/settings.h"
#include "asset/nx/lib/util.h"
static int main_settings(char* cmd, char* file);
int main(int argc, char** argv)
{
int ret;
char* cmd;
char* type;
char* file;
if (argc < 4) {
printf("Usage: %s [cmd: new, dump, unlock] [type: ini, mrank, rank] [file]\n", argv[0]);
return -1;
}
cmd = argv[1];
type = argv[2];
file = argv[3];
if (!strcmp(type, "ini")) {
ret = main_settings(cmd, file);
} else {
fprintf(stderr, "Unknown file type %s\n", type);
ret = -1;
}
return ret;
}
static int main_settings(char* cmd, char* file)
{
int ret;
ret = 0;
if (!strcmp(cmd, "new")) {
struct asset_nx_settings* settings = asset_nx_settings_new();
asset_nx_settings_finalize(settings);
if (!asset_nx_util_save_settings_to_file(file, settings)) {
fprintf(stderr, "Creating nx settings file %s failed\n", file);
ret = -1;
}
free(settings);
} else {
struct asset_nx_settings* settings = asset_nx_util_load_settings_from_file(file);
if (!settings) {
fprintf(stderr, "Loading %s failed", file);
ret = -1;
}
if (!strcmp(cmd, "dump")) {
char* str = asset_nx_settings_to_string(settings);
printf("----------------- settings -----------------\n%s\n", str);
free(str);
} else if (!strcmp(cmd, "unlock")) {
asset_nx_settings_unlock_all(settings);
asset_nx_settings_finalize(settings);
if (!asset_nx_util_save_settings_to_file(file, settings)) {
fprintf(stderr, "Saving nx settings file %s failed\n", file);
ret = -1;
}
} else {
fprintf(stderr, "Unknown command %s\n", cmd);
ret = -1;
}
free(settings);
}
return ret;
}
+93
View File
@@ -0,0 +1,93 @@
#include <string.h>
#include "util/adler32.h"
#include "util/mem.h"
#include "util/str.h"
#include "usb-rank.h"
struct asset_nx2_usb_rank* asset_nx2_usb_rank_new(void)
{
struct asset_nx2_usb_rank* rank;
rank = (struct asset_nx2_usb_rank*)
util_xmalloc(sizeof(struct asset_nx2_usb_rank));
memset(rank, 0, sizeof(struct asset_nx2_usb_rank));
return rank;
}
void asset_nx2_usb_rank_finalize(struct asset_nx2_usb_rank* rank)
{
rank->adler32 = util_adler32_calc(1, ((const uint8_t*) rank) + 4,
sizeof(struct asset_nx2_usb_rank) - 4);
}
char* asset_nx2_usb_rank_to_string(const struct asset_nx2_usb_rank* rank)
{
char* buffer;
char* buffer2;
char* merged;
buffer = (char*) util_xmalloc(128);
memset(buffer, 0, 128);
util_str_format(buffer, 128,
"adler32: 0x%X\n"
"num_rankings: %d\n"
"ranking entries:\n",
rank->adler32,
rank->num_rankings);
for (uint32_t i = 0; i < ASSET_NX2_USB_RANK_MAX_RANK_ENTRIES; i++) {
for (uint32_t j = 0; j < ASSET_NX2_USB_RANK_MAX_STAGES; j++) {
buffer2 = (char*) util_xmalloc(512);
memset(buffer2, 0, 512);
util_str_format(buffer2, 512,
"%d/%d:\n"
"game_mode: %d\n"
"play_order: %d\n"
"play_score: %d\n"
"grade: %d\n"
"mileage: %d\n"
"play_time: %f\n"
"kcal: %f\n"
"v02: %f\n",
i, j,
rank->entries[i][j].game_mode,
rank->entries[i][j].play_order,
rank->entries[i][j].play_score,
rank->entries[i][j].grade,
rank->entries[i][j].mileage,
rank->entries[i][j].play_time,
rank->entries[i][j].kcal,
rank->entries[i][j].v02);
merged = util_str_merge(buffer, buffer2);
free(buffer);
free(buffer2);
buffer = merged;
}
}
return buffer;
}
void asset_nx2_usb_rank_decrypt(uint8_t* buf, size_t len)
{
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_nx2_usb_rank_encrypt(uint8_t* buf, size_t len)
{
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+42
View File
@@ -0,0 +1,42 @@
#ifndef ASSET_NX2_USB_RANK_H
#define ASSET_NX2_USB_RANK_H
#include <stdint.h>
#include <stdlib.h>
#define ASSET_NX2_USB_RANK_SIZE 12296
enum asset_nx2_usb_rank_sizes {
ASSET_NX2_USB_RANK_MAX_STAGES = 3,
ASSET_NX2_USB_RANK_MAX_RANK_ENTRIES = 128,
};
struct asset_nx2_usb_rank_entry {
int32_t game_mode;
int32_t play_order;
int32_t play_score;
int32_t grade;
int32_t mileage;
float play_time;
float kcal;
float v02;
} __attribute__((__packed__));
struct asset_nx2_usb_rank {
uint32_t adler32;
uint32_t num_rankings;
struct asset_nx2_usb_rank_entry entries[ASSET_NX2_USB_RANK_MAX_RANK_ENTRIES][ASSET_NX2_USB_RANK_MAX_STAGES];
} __attribute__((__packed__));
struct asset_nx2_usb_rank* asset_nx2_usb_rank_new(void);
// update checksum and prepare profile to get encrypted
void asset_nx2_usb_rank_finalize(struct asset_nx2_usb_rank* rank);
char* asset_nx2_usb_rank_to_string(const struct asset_nx2_usb_rank* rank);
void asset_nx2_usb_rank_decrypt(uint8_t* buf, size_t len);
void asset_nx2_usb_rank_encrypt(uint8_t* buf, size_t len);
#endif
+370
View File
@@ -0,0 +1,370 @@
#include <stdio.h>
#include <string.h>
#include "util/adler32.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/str.h"
#include "usb-save.h"
static char* asset_nx2_usb_save_time_to_string(
const struct asset_nx2_usb_save_time* time)
{
char* buffer;
buffer = (char*) util_xmalloc(128);
memset(buffer, 0, 128);
util_str_format(buffer, 128,
"year: %d\n"
"month: %d\n"
"day: %d\n"
"hour: %d\n"
"min: %d\n"
"ms: %d\n",
time->year,
time->month,
time->day,
time->hour,
time->min,
time->ms);
return buffer;
}
static char* asset_nx2_usb_save_review_to_string(
const struct asset_nx2_usb_save_review* review)
{
char* buffer;
char* land;
char* mission;
buffer = (char*) util_xmalloc(1024);
memset(buffer, 0, 1024);
land = util_str_buffer((const uint8_t*) review->worldmax_current_land,
ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX);
mission = util_str_buffer((const uint8_t*) review->worldmax_current_mission,
ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX);
util_str_format(buffer, 1024,
">>> review:\n"
"player_id: %s\n"
"mileage: %d\n"
"reward_count: %d\n"
"worldmax_count: %d\n"
"play_count: %d\n"
"worldmax_current_land: %s\n"
"worldmax_current_mission: %s\n"
"kcal: %d\n"
"v02: %d\n",
review->player_id,
review->mileage,
review->reward_count,
review->worldmax_count,
review->play_count,
land,
mission,
review->kcal,
review->v02);
free(land);
free(mission);
return buffer;
}
static char* asset_nx2_usb_save_stats_to_string(
const struct asset_nx2_usb_save_stats* stats)
{
char* merged;
char* buffer;
char* timestamp;
char* tmp;
const size_t buf_size = 512;
merged = (char*) util_xmalloc(buf_size);
memset(merged, 0, buf_size);
buffer = (char*) util_xmalloc(buf_size);
memset(buffer, 0, buf_size);
timestamp = asset_nx2_usb_save_time_to_string(&stats->timestamp);
util_str_format(merged, buf_size,
">>> stats:\n"
"adler32: 0x%X\n"
"usb_serial: %s\n"
"timestamp: %s"
"avatar_id: %d\n"
"rank: %d\n"
"country_id: %d\n"
"player_id: %s\n"
"mileage: %d\n"
"play_count: %d\n"
"kcal: %d\n"
"v02: %d\n"
"worldmax_map_position: 0x%X\n"
"reward_count: %d\n"
"worldmax_count: %d\n",
stats->adler32,
stats->usb_serial,
timestamp,
stats->avatar_id,
stats->rank,
stats->country_id,
stats->player_id,
stats->mileage,
stats->play_count,
stats->kcal,
stats->v02,
stats->worldmax_map_position,
stats->reward_count,
stats->worldmax_count);
free(timestamp);
tmp = util_str_merge(merged, "song_unlocks:\n");
free(merged);
merged = tmp;
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_SONG_MAX; i++) {
util_str_format(buffer, buf_size,
"song slot: %d\n"
"mode: 0x%X\n"
"unused: 0x%X\n"
"song: %d\n"
"reveal: %d\n",
i,
stats->song_unlocks[i].mode,
stats->song_unlocks[i].unused,
stats->song_unlocks[i].song,
stats->song_unlocks[i].reveal);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"mission slot: %d\n"
"clear_flags_directions: 0x%X\n"
"clear_flag_mission: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_mission_unlocks[i].clear_flags_directions,
stats->worldmax_mission_unlocks[i].clear_flag_mission,
stats->worldmax_mission_unlocks[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_high_score %d: %d\n",
i,
stats->worldmax_high_score[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_challenge %d: %d\n",
i,
stats->worldmax_challenge[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_BARRICADE_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_barricade %d:\n"
"flag_open: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_barricade[i].flag_open,
stats->worldmax_barricade[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_EVENT_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_event %d:\n"
"flag_cleared: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_event[i].flag_cleared,
stats->worldmax_event[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_WARP_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_warp %d:\n"
"direction_clear: 0x%X\n"
"clear: %d\n",
"unused: 0x%X\n",
i,
stats->worldmax_warp[i].direction_clear,
stats->worldmax_warp[i].clear,
stats->worldmax_warp[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_current_land %d: %d\n",
i,
stats->worldmax_current_land[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_current_mission %d: %d\n",
i,
stats->worldmax_current_mission[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_SONG_MAX; i++) {
for (uint32_t j = 0; j < ASSET_NX2_USB_SAVE_NUM_MODES; j++) {
util_str_format(buffer, buf_size,
"song_scores %d/%d:\n"
"score: %d\n"
"player_id: %s\n",
i,
j,
stats->song_scores[i][j].score,
stats->song_scores[i][j].player_id);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
}
free(buffer);
return merged;
}
struct asset_nx2_usb_save* asset_nx2_usb_save_new(void)
{
struct asset_nx2_usb_save* save;
char name[10];
save = (struct asset_nx2_usb_save*) util_xmalloc(
sizeof(struct asset_nx2_usb_save));
memset(save, 0, sizeof(struct asset_nx2_usb_save));
sprintf(name, "NX%06d", rand() % 1000000);
strcpy(save->review.player_id, name);
strcpy(save->stats.player_id, name);
/* default world map position */
save->stats.worldmax_map_position = 0xEF001;
/* nx2 release date as default timestamp */
save->stats.timestamp.year = 2008;
save->stats.timestamp.month = 1;
save->stats.timestamp.day = 21;
save->stats.timestamp.hour = 0;
save->stats.timestamp.min = 0;
save->stats.timestamp.ms = 0;
return save;
}
void asset_nx2_usb_save_finalize(struct asset_nx2_usb_save* save)
{
/* checksum update */
save->stats.adler32 = util_adler32_calc(1,
((const uint8_t*) &save->stats) + 4,
sizeof(struct asset_nx2_usb_save_stats) - 4);
}
char* asset_nx2_usb_save_to_string(const struct asset_nx2_usb_save* save)
{
char* review;
char* stats;
char* merged;
review = asset_nx2_usb_save_review_to_string(&save->review);
stats = asset_nx2_usb_save_stats_to_string(&save->stats);
merged = util_str_merge(review, stats);
free(review);
free(stats);
return merged;
}
void asset_nx2_usb_save_decrypt(uint8_t* buf, size_t len)
{
/* skip non encrypted review area */
buf += sizeof(struct asset_nx2_usb_save_review);
len -= sizeof(struct asset_nx2_usb_save_review);
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_nx2_usb_save_encrypt(uint8_t* buf, size_t len)
{
/* skip non encrypted review area */
buf += sizeof(struct asset_nx2_usb_save_review);
len -= sizeof(struct asset_nx2_usb_save_review);
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+122
View File
@@ -0,0 +1,122 @@
#ifndef ASSET_NX2_USB_SAVE_H
#define ASSET_NX2_USB_SAVE_H
#include <stdint.h>
#include <stdlib.h>
#define ASSET_NX2_USB_SAVE_SIZE 30780
enum asset_nx2_usb_save_limits {
ASSET_NX2_USB_SAVE_SERIAL_ID_MAX = 64,
ASSET_NX2_USB_SAVE_PLAYER_ID_MAX = 12,
ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX = 128,
ASSET_NX2_USB_SAVE_SONG_MAX = 256,
ASSET_NX2_USB_SAVE_MISSION_MAX = 1024,
ASSET_NX2_USB_SAVE_BARRICADE_MAX = 16,
ASSET_NX2_USB_SAVE_EVENT_MAX = 128,
ASSET_NX2_USB_SAVE_WARP_MAX = 8,
ASSET_NX2_USB_SAVE_NUM_MODES = 5,
};
struct asset_nx2_usb_save_time {
int16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint16_t ms;
} __attribute__((__packed__));
struct asset_nx2_usb_save_review {
char player_id[ASSET_NX2_USB_SAVE_PLAYER_ID_MAX];
int32_t mileage;
int32_t reward_count;
int32_t worldmax_count;
int32_t play_count;
char worldmax_current_land[ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX];
char worldmax_current_mission[ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX];
int32_t kcal;
int32_t v02;
} __attribute__((__packed__));
struct asset_nx2_usb_save_stats {
uint32_t adler32;
char usb_serial[ASSET_NX2_USB_SAVE_SERIAL_ID_MAX];
struct asset_nx2_usb_save_time timestamp;
int32_t avatar_id;
int32_t rank;
int32_t country_id;
char player_id[ASSET_NX2_USB_SAVE_PLAYER_ID_MAX];
int32_t mileage;
int32_t play_count;
int32_t kcal;
int32_t v02;
int32_t worldmax_map_position;
int32_t reward_count;
int32_t worldmax_count;
struct {
/* bits 0-4 for modes/stepcharts */
uint8_t mode : 5;
/* unused bit */
uint8_t unused : 1;
/* bit 6 song itself */
uint8_t song : 1;
/* bit 7 reveal flag */
uint8_t reveal : 1;
} song_unlocks[ASSET_NX2_USB_SAVE_SONG_MAX] __attribute__((__packed__));
struct {
uint8_t clear_flags_directions : 4;
uint8_t clear_flag_mission : 1;
uint8_t unused : 3;
} worldmax_mission_unlocks[ASSET_NX2_USB_SAVE_MISSION_MAX]
__attribute__((__packed__));
int32_t worldmax_high_score[ASSET_NX2_USB_SAVE_MISSION_MAX];
int32_t worldmax_challenge[ASSET_NX2_USB_SAVE_MISSION_MAX];
struct {
uint8_t flag_open : 1;
uint8_t unused : 7;
} worldmax_barricade[ASSET_NX2_USB_SAVE_BARRICADE_MAX]
__attribute__((__packed__));
struct {
uint8_t flag_cleared : 1;
uint8_t unused : 7;
} worldmax_event[ASSET_NX2_USB_SAVE_EVENT_MAX] __attribute__((__packed__));
struct {
uint8_t direction_clear : 4;
uint8_t clear : 1;
uint8_t unused : 3;
} worldmax_warp[ASSET_NX2_USB_SAVE_WARP_MAX] __attribute__((__packed__));
char worldmax_current_land[ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX];
char worldmax_current_mission[ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX];
struct {
int32_t score;
char player_id[ASSET_NX2_USB_SAVE_PLAYER_ID_MAX];
} song_scores[ASSET_NX2_USB_SAVE_SONG_MAX][ASSET_NX2_USB_SAVE_NUM_MODES]
__attribute__((__packed__));
} __attribute__((__packed__));
struct asset_nx2_usb_save {
struct asset_nx2_usb_save_review review;
struct asset_nx2_usb_save_stats stats;
} __attribute__((__packed__));
struct asset_nx2_usb_save* asset_nx2_usb_save_new(void);
// update checksum and prepare profile to get encrypted
void asset_nx2_usb_save_finalize(struct asset_nx2_usb_save* save);
char* asset_nx2_usb_save_to_string(const struct asset_nx2_usb_save* save);
void asset_nx2_usb_save_decrypt(uint8_t* buf, size_t len);
void asset_nx2_usb_save_encrypt(uint8_t* buf, size_t len);
#endif
+100
View File
@@ -0,0 +1,100 @@
#define LOG_MODULE "asset-nx2-util"
#include <string.h>
#include "util/fs.h"
#include "util/log.h"
#include "util.h"
struct asset_nx2_usb_rank* asset_nx2_usb_rank_load_from_file(const char* path,
bool encrypted)
{
struct asset_nx2_usb_rank* rank;
size_t size;
if (!util_file_load(path, (void**) &rank, &size, false)) {
log_error("Loading nx2 rank file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_nx2_usb_rank)) {
log_error("Invalid size of nx2 rank file %s: %d != %d", path, size,
sizeof(struct asset_nx2_usb_rank));
free(rank);
return NULL;
}
if (encrypted) {
asset_nx2_usb_rank_decrypt((uint8_t*) rank, size);
}
return rank;
}
bool asset_nx2_usb_rank_save_to_file(const char* path,
const struct asset_nx2_usb_rank* rank, bool encrypt)
{
struct asset_nx2_usb_rank rank_enc;
memcpy(&rank_enc, rank, sizeof(struct asset_nx2_usb_rank));
if (encrypt) {
asset_nx2_usb_rank_encrypt((uint8_t*) &rank_enc,
sizeof(struct asset_nx2_usb_rank));
}
if (!util_file_save(path, (void**) &rank_enc,
sizeof(struct asset_nx2_usb_rank))) {
log_error("Storing nx2 rank data to %s failed", path);
return false;
}
return true;
}
struct asset_nx2_usb_save* asset_nx2_usb_save_load_from_file(const char* path,
bool encrypted)
{
struct asset_nx2_usb_save* save;
size_t size;
if (!util_file_load(path, (void**) &save, &size, false)) {
log_error("Loading nx2 save file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_nx2_usb_save)) {
log_error("Invalid size of nx2 save file %s: %d != %d", path, size,
sizeof(struct asset_nx2_usb_save));
free(save);
return NULL;
}
if (encrypted) {
asset_nx2_usb_save_decrypt((uint8_t*) save, size);
}
return save;
}
bool asset_nx2_usb_save_save_to_file(const char* path,
const struct asset_nx2_usb_save* save, bool encrypt)
{
struct asset_nx2_usb_save save_enc;
memcpy(&save_enc, save, sizeof(struct asset_nx2_usb_save));
if (encrypt) {
asset_nx2_usb_save_encrypt((uint8_t*) &save_enc,
sizeof(struct asset_nx2_usb_save));
}
if (!util_file_save(path, (const void*) &save_enc,
sizeof(struct asset_nx2_usb_save))) {
log_error("Storing nx2 save data to %s failed", path);
return false;
}
return true;
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef ASSET_NX2_UTIL_H
#define ASSET_NX2_UTIL_H
#include <stdbool.h>
#include "usb-rank.h"
#include "usb-save.h"
// load from file and decrypt, returns decrypted struct
struct asset_nx2_usb_rank* asset_nx2_usb_rank_load_from_file(const char* path, bool encrypted);
// takes decrypted rank data, encrypts it and writes it to file
bool asset_nx2_usb_rank_save_to_file(const char* path, const struct asset_nx2_usb_rank* rank, bool encrypt);
// load from file and decrypt, returns decrypted struct
struct asset_nx2_usb_save* asset_nx2_usb_save_load_from_file(const char* path, bool encrypted);
// takes decrypted save data, encrypts it and writes it to file
bool asset_nx2_usb_save_save_to_file(const char* path, const struct asset_nx2_usb_save* save, bool encrypt);
#endif
+154
View File
@@ -0,0 +1,154 @@
/**
* Tool for NX2 profiles: decrypt, encrypt, print profile data
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asset/nx2/lib/util.h"
#include "util/str.h"
int main(int argc, char** argv)
{
int ret;
char* rank_path;
char* save_path;
if (argc < 3) {
printf("Usage: %s [cmd: new, dec, enc, dump] "
"[path containing nx2rank.bin nx2save.bin]\n", argv[0]);
return -1;
}
ret = 0;
rank_path = util_str_merge(argv[2], "nx2rank.bin");
save_path = util_str_merge(argv[2], "nx2save.bin");
if (!strcmp(argv[1], "new")) {
struct asset_nx2_usb_rank* rank = asset_nx2_usb_rank_new();
struct asset_nx2_usb_save* save = asset_nx2_usb_save_new();
asset_nx2_usb_rank_finalize(rank);
asset_nx2_usb_save_finalize(save);
if (!asset_nx2_usb_rank_save_to_file(rank_path, rank, true)) {
fprintf(stderr, "Creating rank file %s failed\n", rank_path);
ret = -2;
}
if (!asset_nx2_usb_save_save_to_file(save_path, save, true)) {
fprintf(stderr, "Creating save file %s failed\n", save_path);
ret = -3;
}
free(rank);
free(save);
} else {
struct asset_nx2_usb_rank* rank;
struct asset_nx2_usb_save* save;
bool encrypted;
if (!strcmp(argv[1], "enc")) {
char* tmp;
encrypted = false;
tmp = util_str_merge(rank_path, ".dec");
free(rank_path);
rank_path = tmp;
tmp = util_str_merge(save_path, ".dec");
free(save_path);
save_path = tmp;
} else {
encrypted = true;
}
rank = asset_nx2_usb_rank_load_from_file(rank_path, encrypted);
save = asset_nx2_usb_save_load_from_file(save_path, encrypted);
if (!rank) {
fprintf(stderr, "Loading %s failed", rank_path);
ret = -4;
goto cleanup;
}
if (!save) {
fprintf(stderr, "Loading %s failed", save_path);
ret = -5;
free(rank);
goto cleanup;
}
if (!strcmp(argv[1], "dec")) {
char* rank_path_dec;
char* save_path_dec;
rank_path_dec = util_str_merge(rank_path, ".dec");
save_path_dec = util_str_merge(save_path, ".dec");
if (!asset_nx2_usb_rank_save_to_file(rank_path_dec, rank, false)) {
fprintf(stderr, "Saving decrypted rank file to %s failed\n",
rank_path_dec);
ret = -6;
}
if (!asset_nx2_usb_save_save_to_file(save_path_dec, save, false)) {
fprintf(stderr, "Saving decrypted save file to %s failed\n",
save_path_dec);
ret = -7;
}
free(rank_path_dec);
free(save_path_dec);
} else if (!strcmp(argv[1], "enc")) {
char* rank_path_enc;
char* save_path_enc;
rank_path_enc = util_str_merge(rank_path, ".enc");
save_path_enc = util_str_merge(save_path, ".enc");
if (!asset_nx2_usb_rank_save_to_file(rank_path_enc, rank, true)) {
fprintf(stderr, "Saving encrypted rank file to %s failed\n",
rank_path_enc);
ret = -6;
}
if (!asset_nx2_usb_save_save_to_file(save_path_enc, save, true)) {
fprintf(stderr, "Saving encrypted save file to %s failed\n",
save_path_enc);
ret = -7;
}
free(rank_path_enc);
free(save_path_enc);
} else if (!strcmp(argv[1], "dump")) {
char* rank_str;
char* save_str;
rank_str = asset_nx2_usb_rank_to_string(rank);
save_str = asset_nx2_usb_save_to_string(save);
printf("----------------- rank -----------------\n"
"%s----------------- save -----------------\n%s",
rank_str, save_str);
free(rank_str);
free(save_str);
} else {
fprintf(stderr, "Unknown command %s\n", argv[1]);
ret = -4;
}
free(rank);
free(save);
}
cleanup:
free(rank_path);
free(save_path);
return ret;
}
+103
View File
@@ -0,0 +1,103 @@
#include <stdio.h>
#include <string.h>
#include "util/adler32.h"
#include "util/mem.h"
#include "util/str.h"
#include "usb-rank.h"
struct asset_nxa_usb_rank* asset_nxa_usb_rank_new(void)
{
struct asset_nxa_usb_rank* rank;
rank = (struct asset_nxa_usb_rank*)
util_xmalloc(sizeof(struct asset_nxa_usb_rank));
memset(rank, 0, sizeof(struct asset_nxa_usb_rank));
return rank;
}
void asset_nxa_usb_rank_finalize(struct asset_nxa_usb_rank* rank)
{
rank->adler32 = util_adler32_calc(1, ((const uint8_t*) rank) + 4,
sizeof(struct asset_nxa_usb_rank) - 4);
}
char* asset_nxa_usb_rank_to_string(const struct asset_nxa_usb_rank* rank)
{
char* buffer;
char* buffer2;
char* merged;
char* usb_ser;
buffer = (char*) util_xmalloc(512);
memset(buffer, 0, 512);
usb_ser = util_str_buffer((const uint8_t*) rank->usb_serial,
ASSET_NXA_USB_RANK_USB_SERIAL_LEN);
util_str_format(buffer, 512,
"adler32: 0x%X\n"
// "usb_serial: %s\n",
"num_rankings: %d\n"
"ranking entries:\n",
rank->adler32,
// usb_ser,
rank->num_rankings);
for (uint32_t i = 0; i < ASSET_NXA_USB_RANK_MAX_RANK_ENTRIES; i++) {
for (uint32_t j = 0; j < ASSET_NXA_USB_RANK_MAX_STAGES; j++) {
buffer2 = (char*) util_xmalloc(512);
memset(buffer2, 0, 512);
util_str_format(buffer2, 512,
"%d/%d:\n"
"game_mode: %d\n"
"play_order: %d\n"
"play_score: %d\n"
"grade: %d\n"
"mileage: %d\n"
"play_time: %f\n"
"kcal: %f\n"
"v02: %f\n",
i, j,
rank->entries[i][j].game_mode,
rank->entries[i][j].play_order,
rank->entries[i][j].play_score,
rank->entries[i][j].grade,
rank->entries[i][j].mileage,
rank->entries[i][j].play_time,
rank->entries[i][j].kcal,
rank->entries[i][j].v02);
merged = util_str_merge(buffer, buffer2);
free(buffer);
free(buffer2);
buffer = merged;
}
}
free(usb_ser);
return buffer;
}
void asset_nxa_usb_rank_decrypt(uint8_t* buf, size_t len)
{
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_nxa_usb_rank_encrypt(uint8_t* buf, size_t len)
{
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef ASSET_NXA_USB_RANK_H
#define ASSET_NXA_USB_RANK_H
#include <stdint.h>
#include <stdlib.h>
enum asset_nxa_usb_rank_sizes {
ASSET_NXA_USB_RANK_MAX_STAGES = 3,
ASSET_NXA_USB_RANK_MAX_RANK_ENTRIES = 256,
ASSET_NXA_USB_RANK_USB_SERIAL_LEN = 64,
};
struct asset_nxa_usb_rank_entry {
int32_t game_mode;
int32_t play_order;
int32_t play_score;
int32_t grade;
int32_t mileage;
float play_time;
float kcal;
float v02;
} __attribute__((__packed__));
struct asset_nxa_usb_rank {
uint32_t adler32;
char usb_serial[ASSET_NXA_USB_RANK_USB_SERIAL_LEN];
uint32_t num_rankings;
struct asset_nxa_usb_rank_entry
entries[ASSET_NXA_USB_RANK_MAX_RANK_ENTRIES][ASSET_NXA_USB_RANK_MAX_STAGES];
} __attribute__((__packed__));
struct asset_nxa_usb_rank* asset_nxa_usb_rank_new(void);
// update checksum and prepare profile to get encrypted
void asset_nxa_usb_rank_finalize(struct asset_nxa_usb_rank* rank);
char* asset_nxa_usb_rank_to_string(const struct asset_nxa_usb_rank* rank);
void asset_nxa_usb_rank_decrypt(uint8_t* buf, size_t len);
void asset_nxa_usb_rank_encrypt(uint8_t* buf, size_t len);
#endif
+457
View File
@@ -0,0 +1,457 @@
#include <stdio.h>
#include <string.h>
#include "util/adler32.h"
#include "util/mem.h"
#include "util/str.h"
#include "usb-save.h"
static char* asset_nxa_usb_save_time_to_string(
const struct asset_nxa_usb_save_time* time)
{
char* buffer;
buffer = (char*) util_xmalloc(128);
memset(buffer, 0, 128);
util_str_format(buffer, 128,
"year: %d\n"
"month: %d\n"
"day: %d\n"
"hour: %d\n"
"min: %d\n"
"ms: %d\n",
time->year,
time->month,
time->day,
time->hour,
time->min,
time->ms);
return buffer;
}
static char* asset_nxa_usb_save_review_to_string(
const struct asset_nxa_usb_save_review* review)
{
char* buffer;
char* land;
char* mission;
char* timestamp;
buffer = (char*) util_xmalloc(1024);
memset(buffer, 0, 1024);
land = util_str_buffer((const uint8_t*) review->worldmax_current_land,
ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX);
mission = util_str_buffer((const uint8_t*) review->worldmax_current_mission,
ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX);
timestamp = asset_nxa_usb_save_time_to_string(&review->timestamp);
util_str_format(buffer, 1024,
">>> review:\n"
"player_id: %s\n"
"mileage: %d\n"
"reward_count: %d\n"
"worldmax_count: %d\n"
"play_count: %d\n"
"worldmax_current_land: %s\n"
"worldmax_current_mission: %s\n"
"kcal: %d\n"
"v02: %d\n",
"timestamp: \n%s",
"running_step: %d\n",
"play_time_minutes: %f\n",
"total_completion_percentage: %f\n",
"arcade_percentage: %f\n",
"brain_shower_percentage: %f\n",
"special_percentage: %f\n",
review->player_id,
review->mileage,
review->reward_count,
review->worldmax_count,
review->play_count,
land,
mission,
review->kcal,
review->v02,
timestamp,
review->running_step,
review->play_time_minutes,
review->total_completion_percentage,
review->arcade_percentage,
review->brain_shower_percentage,
review->special_percentage);
free(land);
free(mission);
free(timestamp);
return buffer;
}
static char* asset_nxa_usb_save_stats_to_string(
const struct asset_nxa_usb_save_stats* stats)
{
char* merged;
char* buffer;
char* timestamp;
char* tmp;
char* tmp2;
const size_t buf_size = 16384;
merged = (char*) util_xmalloc(buf_size);
memset(merged, 0, buf_size);
buffer = (char*) util_xmalloc(buf_size);
memset(buffer, 0, buf_size);
timestamp = asset_nxa_usb_save_time_to_string(&stats->timestamp);
util_str_format(merged, buf_size,
">>> stats:\n"
"adler32: 0x%X\n"
"usb_serial: %s\n"
"timestamp: %s"
"avatar_id: %d\n"
"rank: %d\n"
"country_id: %d\n"
"player_id: %s\n"
"mileage: %d\n"
"play_count: %d\n"
"kcal: %d\n"
"v02: %d\n"
"worldmax_map_position: 0x%X\n"
"reward_count: %d\n"
"worldmax_count: %d\n",
stats->adler32,
stats->usb_serial,
timestamp,
stats->avatar_id,
stats->rank,
stats->country_id,
stats->player_id,
stats->mileage,
stats->play_count,
stats->kcal,
stats->v02,
stats->worldmax_map_position,
stats->reward_count,
stats->worldmax_count);
free(timestamp);
tmp = util_str_merge(merged, "song_unlocks:\n");
free(merged);
merged = tmp;
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_SONG_MAX; i++) {
util_str_format(buffer, buf_size,
"song slot: %d\n"
"mode: 0x%X\n"
"unused: 0x%X\n"
"song: %d\n"
"reveal: %d\n",
i,
stats->song_unlocks[i].mode,
stats->song_unlocks[i].unused,
stats->song_unlocks[i].song,
stats->song_unlocks[i].reveal);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"mission slot: %d\n"
"clear_flags_directions: 0x%X\n"
"clear_flag_mission: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_mission_unlocks[i].clear_flags_directions,
stats->worldmax_mission_unlocks[i].clear_flag_mission,
stats->worldmax_mission_unlocks[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_high_score %d: %d\n",
i,
stats->worldmax_high_score[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_challenge %d: %d\n",
i,
stats->worldmax_challenge[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_BARRICADE_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_barricade %d:\n"
"flag_open: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_barricade[i].flag_open,
stats->worldmax_barricade[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_EVENT_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_event %d:\n"
"flag_cleared: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_event[i].flag_cleared,
stats->worldmax_event[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_WARP_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_warp %d:\n"
"direction_clear: 0x%X\n"
"clear: %d\n",
"unused: 0x%X\n",
i,
stats->worldmax_warp[i].direction_clear,
stats->worldmax_warp[i].clear,
stats->worldmax_warp[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_current_land %d: %d\n",
i,
stats->worldmax_current_land[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_current_mission %d: %d\n",
i,
stats->worldmax_current_mission[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_SONG_MAX; i++) {
for (uint32_t j = 0; j < ASSET_NXA_USB_SAVE_NUM_MODES; j++) {
util_str_format(buffer, buf_size,
"song_scores %d/%d:\n"
"score: %d\n"
"player_id: %s\n",
i,
j,
stats->song_scores[i][j].score,
stats->song_scores[i][j].player_id);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
}
/* ------------- */
util_str_format(buffer, buf_size,
"security_dongle_mfgid 0x%X\n"
"unlock_signal: 0x%X\n"
"running_steps: %d\n"
"total_play_time_min: %f\n"
"total_completion_percentage: %f\n"
"arcade_completion_percentage: %f\n"
"brain_shower_completion_percentage: %f\n"
"special_stage_completion_percentage: %f\n",
stats->security_dongle_mfgid,
stats->unlock_signal,
stats->running_steps,
stats->total_play_time_min,
stats->total_completion_percentage,
stats->arcade_completion_percentage,
stats->brain_shower_completion_percentage,
stats->special_stage_completion_percentage);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
/* ------------- */
tmp2 = util_str_buffer(stats->unkn_some_map_scores_to_song_ids,
4096);
util_str_format(buffer, buf_size,
"unkn_some_map_scores_to_song_ids: %s\n",
tmp2);
tmp = util_str_merge(merged, buffer);
free(tmp2);
free(merged);
merged = tmp;
/* ------------- */
tmp2 = util_str_buffer(stats->unkn2, sizeof(stats->unkn2));
util_str_format(buffer, buf_size,
"unkn2: %s\n",
tmp2);
tmp = util_str_merge(merged, buffer);
free(tmp2);
free(merged);
merged = tmp;
/* ------------- */
util_str_format(buffer, buf_size,
"break_item_slot 0x%X\n"
"func_item_slot: 0x%X\n"
"bga_item_slot: 0x%X\n"
"time_item_slot: 0x%X\n"
"unknown: 0x%X\n",
stats->break_item_slot,
stats->func_item_slot,
stats->bga_item_slot,
stats->time_item_slot,
stats->unknown);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
free(buffer);
return merged;
}
struct asset_nxa_usb_save* asset_nxa_usb_save_new(void)
{
struct asset_nxa_usb_save* save;
char name[10];
save = (struct asset_nxa_usb_save*) util_xmalloc(
sizeof(struct asset_nxa_usb_save));
memset(save, 0, sizeof(struct asset_nxa_usb_save));
sprintf(name, "NX%06d", rand() % 1000000);
strcpy(save->review.player_id, name);
strcpy(save->stats.player_id, name);
/* default world map position */
save->stats.worldmax_map_position = 0xEF001;
/* nxa release date as default timestamp */
save->stats.timestamp.year = 2008;
save->stats.timestamp.month = 12;
save->stats.timestamp.day = 26;
save->stats.timestamp.hour = 0;
save->stats.timestamp.min = 0;
save->stats.timestamp.ms = 0;
return save;
}
void asset_nxa_usb_save_finalize(struct asset_nxa_usb_save* save)
{
/* checksum update, expected size for area to checksum: 90540 */
save->stats.adler32 = util_adler32_calc(1,
((const uint8_t*) &save->stats) + 4,
sizeof(struct asset_nxa_usb_save_stats) - 4);
}
char* asset_nxa_usb_save_to_string(const struct asset_nxa_usb_save* save)
{
char* review;
char* stats;
char* merged;
review = asset_nxa_usb_save_review_to_string(&save->review);
stats = asset_nxa_usb_save_stats_to_string(&save->stats);
merged = util_str_merge(review, stats);
free(review);
free(stats);
return merged;
}
void asset_nxa_usb_save_decrypt(uint8_t* buf, size_t len)
{
/* skip non encrypted review area */
buf += sizeof(struct asset_nxa_usb_save_review);
len -= sizeof(struct asset_nxa_usb_save_review);
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_nxa_usb_save_encrypt(uint8_t* buf, size_t len)
{
/* skip non encrypted review area */
buf += sizeof(struct asset_nxa_usb_save_review);
len -= sizeof(struct asset_nxa_usb_save_review);
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+275
View File
@@ -0,0 +1,275 @@
#ifndef ASSET_NXA_USB_SAVE_H
#define ASSET_NXA_USB_SAVE_H
#include <stdint.h>
#include <stdlib.h>
enum asset_nxa_usb_save_limits {
ASSET_NXA_USB_SAVE_SERIAL_ID_MAX = 64,
ASSET_NXA_USB_SAVE_PLAYER_ID_MAX = 12,
ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX = 128,
ASSET_NXA_USB_SAVE_SONG_MAX = 256,
ASSET_NXA_USB_SAVE_MISSION_MAX = 1024,
ASSET_NXA_USB_SAVE_BARRICADE_MAX = 16,
ASSET_NXA_USB_SAVE_EVENT_MAX = 128,
ASSET_NXA_USB_SAVE_WARP_MAX = 8,
ASSET_NXA_USB_SAVE_NUM_MODES = 5,
};
struct asset_nxa_usb_save_time {
int16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint16_t ms;
} __attribute__((__packed__));
/* Not encrypted and read only, offset 0x00 to 0x144 (excl) */
struct asset_nxa_usb_save_review {
/* 0x00, 12 byte char array: max char length is 8 which gets displayed
ingame */
char player_id[ASSET_NXA_USB_SAVE_PLAYER_ID_MAX];
/* 0x0C, 4 byte int */
uint32_t mileage;
/* 0x10, 4 byte int: achieved reward count, max 65 */
uint32_t reward_count;
/* 0x14, 4 byte int: successful mission count, max 634 */
uint32_t worldmax_count;
/* 0x18, 4 byte int */
uint32_t play_count;
/* 0x1C, 128 byte char array */
char worldmax_current_land[ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX];
/* 0x9C, 128 byte char array */
char worldmax_current_mission[ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX];
/* 0x11C, 4 byte int */
uint32_t kcal;
/* 0x120, 4 byte int */
uint32_t v02;
/* 0x124, 8 byte */
struct asset_nxa_usb_save_time timestamp;
/* 0x12C, 4 byte int */
uint32_t running_step;
/* 0x130, 4 byte float */
float play_time_minutes;
/* 0x134, 4 byte float */
float total_completion_percentage;
/* 0x138, 4 byte float */
float arcade_percentage;
/* 0x13C, 4 byte float */
float brain_shower_percentage;
/* 0x140, 4 byte float */
float special_percentage;
} __attribute__((__packed__));
struct asset_nxa_usb_save_stats {
/* 0x144, 4 byte int */
uint32_t adler32;
/* 0x148, 64 byte */
char usb_serial[ASSET_NXA_USB_SAVE_SERIAL_ID_MAX];
/* 0x188, 8 byte */
struct asset_nxa_usb_save_time timestamp;
/* 0x190, 4 byte int */
int32_t avatar_id;
/* 0x194, 4 byte int: setting this non null will give you a trophy */
int32_t rank;
/* 0x198, 4 byte int: seems unused */
int32_t country_id;
/* 0x19C, 12 byte char array */
char player_id[ASSET_NXA_USB_SAVE_PLAYER_ID_MAX];
/* 0x1A8, 4 byte int */
int32_t mileage;
/* 0x1AD, 4 byte int */
int32_t play_count;
/* 0x1B0, 4 byte int */
int32_t kcal;
/* 0x1B4, 4 byte int */
int32_t v02;
/* 0x1B8, 4 byte int */
int32_t worldmax_map_position;
/* 0x1BC, 4 byte int: achieved reward count, max 65 */
int32_t reward_count;
/* 0x1C0, 4 byte int: successful mission count, max 634 */
int32_t worldmax_count;
/* 0x1C4, 256 bytes */
struct {
/* bits 0-4 for modes/stepcharts */
uint8_t mode : 5;
/* unused bit */
uint8_t unused : 1;
/* bit 6 song itself */
uint8_t song : 1;
/* bit 7 reveal flag */
uint8_t reveal : 1;
} song_unlocks[ASSET_NXA_USB_SAVE_SONG_MAX] __attribute__((__packed__));
/* 0x2C4, 1024 bytes */
struct {
uint8_t clear_flags_directions : 4;
uint8_t clear_flag_mission : 1;
uint8_t unused : 3;
} worldmax_mission_unlocks[ASSET_NXA_USB_SAVE_MISSION_MAX]
__attribute__((__packed__));
/* 0x6C4, 4 byte int * 1024: high score for each mission */
int32_t worldmax_high_score[ASSET_NXA_USB_SAVE_MISSION_MAX];
/* 0x16C4, 4 byte int * 1024 */
int32_t worldmax_challenge[ASSET_NXA_USB_SAVE_MISSION_MAX];
/* 0x26C4, 16 bytes
0x00 for barricade clear and 0x01 for block */
struct {
uint8_t flag_open : 1;
uint8_t unused : 7;
} worldmax_barricade[ASSET_NXA_USB_SAVE_BARRICADE_MAX]
__attribute__((__packed__));
/* 0x26D4, 128 bytes
0x00 for event clear and 0x01 for not cleared */
struct {
uint8_t flag_cleared : 1;
uint8_t unused : 7;
} worldmax_event[ASSET_NXA_USB_SAVE_EVENT_MAX] __attribute__((__packed__));
/* 0x2754, 8 bytes
bit 0~3: clear bit for each direction bit:4 clear */
struct {
uint8_t direction_clear : 4;
uint8_t clear : 1;
uint8_t unused : 3;
} worldmax_warp[ASSET_NXA_USB_SAVE_WARP_MAX] __attribute__((__packed__));
/* 0x275C, 128 byte char array */
char worldmax_current_land[ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX];
/* 0x27DC, 128 byte char array */
char worldmax_current_mission[ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX];
/* 0x285C */
struct {
int32_t score;
char player_id[ASSET_NXA_USB_SAVE_PLAYER_ID_MAX];
} song_scores[ASSET_NXA_USB_SAVE_SONG_MAX][ASSET_NXA_USB_SAVE_NUM_MODES]
__attribute__((__packed__));
/* 0x785C, 4 byte int */
uint32_t security_dongle_mfgid;
/* 0x7860, 4 byte int: <=3 Tells game to unlock new song */
uint32_t unlock_signal;
/* 0x7864, 4 byte int */
uint32_t running_steps;
/* 0x7868, 4 byte float */
float total_play_time_min;
/* 0x786C, 4 byte float */
float total_completion_percentage;
/* 0x7870, 4 byte float */
float arcade_completion_percentage;
/* 0x7874, 4 byte float */
float brain_shower_completion_percentage;
/* 0x7878, 4 byte float */
float special_stage_completion_percentage;
/* 0x787C TODO unknown
0x787C, 1 byte char * 4096 - Used by GetSongIndex -
Assuming Maps Scores to Song IDs */
uint8_t unkn_some_map_scores_to_song_ids[4096];
/* 0x887C TODO unknown
0x887C, 1 byte char * 576 - Song Locks/Unlocks - 0x40 Unlocks (Special Zone, etc.)
0x8A70 , 1 byte char * 36 - WorldMax Keys and various other events for completion
0x8A9C, 8 byte char* - A Worldmax ID (Perhaps one to mark progress in Space)
0x8AA4, 1 byte char * 24 - More WorldMax Event Switches */
uint8_t unkn2[576];
/* 0x8ABC, 4 byte int: possible values
0x1 - grey
0x2 - red
0x3 - yellow
0x4 - green
0x5 - Dark Green
0x6 - aqua
0x7 - blue
0x8 - purple
0x9 - pink
0xA - light grey
0xB - Black
0xC - Orange
*/
uint32_t break_item_slot;
/* 0x8AC0, 4 byte int
0x1 - Shield
0x2 - PermaShield
0x3 - Line Searcher
0x4 - Perma Line Searcher
0x5 - Life Up
0x6 - Perma Life Up
0x7 - Lucky
0x8 - Perma Lucky
0x9 - Portal Pass
*/
uint32_t func_item_slot;
/* 0x8AC4, 4 byte int
0x1 - BGA OFF
0x2 - Perma BGA OFF
*/
uint32_t bga_item_slot;
/* 0x8AC4, 4 byte int
0x1 - 10+
0x2 - 20+
0x3 - 30+
*/
uint32_t time_item_slot;
/* 0x8AC8, 4 byte int */
uint32_t unknown;
/* 0x8ACC, unknown stuff: More WorldMax Event Switches (Unused)? */
uint8_t unknown2[12];
/* 0x8AD8, 4 byte float - Unknown What this Does (Unused) */
float unknown3;
/* 0x8ADC, totally unknown stuff, just nulls */
uint8_t unknown4[0x7F8];
/* 0x92D4 */
uint8_t unknown5[0xD01C];
/* TODO missing switches for dark/light world stuff
0x8ACC, 1 byte char - More WorldMax Event Switches (Unused)
0x8AD8 , 4 byte float - Unknown What this Does (Unused)
0x92D4, High Score Table [See Below]
0x132D0, 5216 bytes - Unknown Table (Unused)
0x162D0, 24 bytes - Unknown Footer (Unused by Game - Most Likely PumBI)
*/
} __attribute__((__packed__));
struct asset_nxa_usb_save {
struct asset_nxa_usb_save_review review;
struct asset_nxa_usb_save_stats stats;
} __attribute__((__packed__));
struct asset_nxa_usb_save* asset_nxa_usb_save_new(void);
/* update checksum and prepare profile to get encrypted */
void asset_nxa_usb_save_finalize(struct asset_nxa_usb_save* save);
char* asset_nxa_usb_save_to_string(const struct asset_nxa_usb_save* save);
void asset_nxa_usb_save_decrypt(uint8_t* buf, size_t len);
void asset_nxa_usb_save_encrypt(uint8_t* buf, size_t len);
#endif
+100
View File
@@ -0,0 +1,100 @@
#define LOG_MODULE "asset-nxa-util"
#include <string.h>
#include "util/fs.h"
#include "util/log.h"
#include "util.h"
struct asset_nxa_usb_rank* asset_nxa_usb_rank_load_from_file(const char* path,
bool encrypted)
{
struct asset_nxa_usb_rank* rank;
size_t size;
if (!util_file_load(path, (void**) &rank, &size, false)) {
log_error("Loading nxa rank file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_nxa_usb_rank)) {
log_error("Invalid size of nxa rank file %s: %d != %d", path, size,
sizeof(struct asset_nxa_usb_rank));
free(rank);
return NULL;
}
if (encrypted) {
asset_nxa_usb_rank_decrypt((uint8_t*) rank, size);
}
return rank;
}
bool asset_nxa_usb_rank_save_to_file(const char* path,
const struct asset_nxa_usb_rank* rank, bool encrypt)
{
struct asset_nxa_usb_rank rank_enc;
memcpy(&rank_enc, rank, sizeof(struct asset_nxa_usb_rank));
if (encrypt) {
asset_nxa_usb_rank_encrypt((uint8_t*) &rank_enc,
sizeof(struct asset_nxa_usb_rank));
}
if (!util_file_save(path, (void**) &rank_enc,
sizeof(struct asset_nxa_usb_rank))) {
log_error("Storing nxa rank data to %s failed", path);
return false;
}
return true;
}
struct asset_nxa_usb_save* asset_nxa_usb_save_load_from_file(const char* path,
bool encrypted)
{
struct asset_nxa_usb_save* save;
size_t size;
if (!util_file_load(path, (void**) &save, &size, false)) {
log_error("Loading nxa save file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_nxa_usb_save)) {
log_error("Invalid size of nxa save file %s: %d != %d", path, size,
sizeof(struct asset_nxa_usb_save));
free(save);
return NULL;
}
if (encrypted) {
asset_nxa_usb_save_decrypt((uint8_t*) save, size);
}
return save;
}
bool asset_nxa_usb_save_save_to_file(const char* path,
const struct asset_nxa_usb_save* save, bool encrypt)
{
struct asset_nxa_usb_save save_enc;
memcpy(&save_enc, save, sizeof(struct asset_nxa_usb_save));
if (encrypt) {
asset_nxa_usb_save_encrypt((uint8_t*) &save_enc,
sizeof(struct asset_nxa_usb_save));
}
if (!util_file_save(path, (const void*) &save_enc,
sizeof(struct asset_nxa_usb_save))) {
log_error("Storing nxa save data to %s failed", path);
return false;
}
return true;
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef ASSET_NXA_UTIL_H
#define ASSET_NXA_UTIL_H
#include <stdbool.h>
#include "usb-rank.h"
#include "usb-save.h"
// load from file and decrypt, returns decrypted struct
struct asset_nxa_usb_rank* asset_nxa_usb_rank_load_from_file(const char* path, bool encrypted);
// takes decrypted rank data, encrypts it and writes it to file
bool asset_nxa_usb_rank_save_to_file(const char* path, const struct asset_nxa_usb_rank* rank, bool encrypt);
// load from file and decrypt, returns decrypted struct
struct asset_nxa_usb_save* asset_nxa_usb_save_load_from_file(const char* path, bool encrypted);
// takes decrypted save data, encrypts it and writes it to file
bool asset_nxa_usb_save_save_to_file(const char* path, const struct asset_nxa_usb_save* save, bool encrypt);
#endif
+154
View File
@@ -0,0 +1,154 @@
/**
* Tool for NXA profiles: decrypt, encrypt, print profile data
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asset/nxa/lib/util.h"
#include "util/str.h"
int main(int argc, char** argv)
{
int ret;
char* rank_path;
char* save_path;
if (argc < 3) {
printf("Usage: %s [cmd: new, dec, enc, dump] "
"[path containing nxarank.bin nxasave.bin]\n", argv[0]);
return -1;
}
ret = 0;
rank_path = util_str_merge(argv[2], "nxarank.bin");
save_path = util_str_merge(argv[2], "nxasave.bin");
if (!strcmp(argv[1], "new")) {
struct asset_nxa_usb_rank* rank = asset_nxa_usb_rank_new();
struct asset_nxa_usb_save* save = asset_nxa_usb_save_new();
asset_nxa_usb_rank_finalize(rank);
asset_nxa_usb_save_finalize(save);
if (!asset_nxa_usb_rank_save_to_file(rank_path, rank, true)) {
fprintf(stderr, "Creating rank file %s failed\n", rank_path);
ret = -2;
}
if (!asset_nxa_usb_save_save_to_file(save_path, save, true)) {
fprintf(stderr, "Creating save file %s failed\n", save_path);
ret = -3;
}
free(rank);
free(save);
} else {
struct asset_nxa_usb_rank* rank;
struct asset_nxa_usb_save* save;
bool encrypted;
if (!strcmp(argv[1], "enc")) {
char* tmp;
encrypted = false;
tmp = util_str_merge(rank_path, ".dec");
free(rank_path);
rank_path = tmp;
tmp = util_str_merge(save_path, ".dec");
free(save_path);
save_path = tmp;
} else {
encrypted = true;
}
rank = asset_nxa_usb_rank_load_from_file(rank_path, encrypted);
save = asset_nxa_usb_save_load_from_file(save_path, encrypted);
if (!rank) {
fprintf(stderr, "Loading %s failed", rank_path);
ret = -4;
goto cleanup;
}
if (!save) {
fprintf(stderr, "Loading %s failed", save_path);
ret = -5;
free(rank);
goto cleanup;
}
if (!strcmp(argv[1], "dec")) {
char* rank_path_dec;
char* save_path_dec;
rank_path_dec = util_str_merge(rank_path, ".dec");
save_path_dec = util_str_merge(save_path, ".dec");
if (!asset_nxa_usb_rank_save_to_file(rank_path_dec, rank, false)) {
fprintf(stderr, "Saving decrypted rank file to %s failed\n",
rank_path_dec);
ret = -6;
}
if (!asset_nxa_usb_save_save_to_file(save_path_dec, save, false)) {
fprintf(stderr, "Saving decrypted save file to %s failed\n",
save_path_dec);
ret = -7;
}
free(rank_path_dec);
free(save_path_dec);
} else if (!strcmp(argv[1], "enc")) {
char* rank_path_enc;
char* save_path_enc;
rank_path_enc = util_str_merge(rank_path, ".enc");
save_path_enc = util_str_merge(save_path, ".enc");
if (!asset_nxa_usb_rank_save_to_file(rank_path_enc, rank, true)) {
fprintf(stderr, "Saving encrypted rank file to %s failed\n",
rank_path_enc);
ret = -6;
}
if (!asset_nxa_usb_save_save_to_file(save_path_enc, save, true)) {
fprintf(stderr, "Saving encrypted save file to %s failed\n",
save_path_enc);
ret = -7;
}
free(rank_path_enc);
free(save_path_enc);
} else if (!strcmp(argv[1], "dump")) {
char* rank_str;
char* save_str;
rank_str = asset_nxa_usb_rank_to_string(rank);
save_str = asset_nxa_usb_save_to_string(save);
printf("----------------- rank -----------------\n"
"%s----------------- save -----------------\n%s",
rank_str, save_str);
free(rank_str);
free(save_str);
} else {
fprintf(stderr, "Unknown command %s\n", argv[1]);
ret = -4;
}
free(rank);
free(save);
}
cleanup:
free(rank_path);
free(save_path);
return ret;
}
+87
View File
@@ -0,0 +1,87 @@
#include <string.h>
#include "asset/x2/lib/settings.h"
#include "util/adler32.h"
#include "util/mem.h"
static const char* _asset_x2_settings_defaut_high_score_names[] = {
"MKII",
"YAHP",
"KANN",
"ZINY",
"PIAH",
"AHN ",
"PIYO",
"HIKO",
"HALO",
"BAJI",
"YUTE",
"SS ",
"SIHO",
"AZKI",
"JUNE",
"GOON",
"ARI*",
"TAMA",
"YSMA",
"PIU "
};
struct asset_x2_settings* asset_x2_settings_new(void)
{
struct asset_x2_settings* settings;
settings = util_xmalloc(sizeof(struct asset_x2_settings));
memset(settings->ff_pad, 0xFF, sizeof(settings->ff_pad));
memset(settings->unkn, 0, sizeof(settings->unkn));
settings->unlock_canond_full = 0;
settings->total_uptime = 0;
settings->unkn69 = 0x69;
memset(settings->song_blacklist, 0, ASSET_X2_SETTINGS_MAX_SONGS);
memset(settings->unkn3, 0, sizeof(settings->unkn3));
memcpy(settings->ident, "EX02", 4);
/* Updated using finalize */
settings->adler32 = 0;
memset(settings->song_play_count, 0, sizeof(uint16_t) * ASSET_X2_SETTINGS_MAX_SONGS);
settings->padding = 0;
for (uint8_t i = 0; i < ASSET_X2_SETTINGS_HIGH_SCORE_COUNT; i++) {
settings->high_score_scores[i] = 10000u - (500u * i);
memcpy(&settings->high_score_names[i], &_asset_x2_settings_defaut_high_score_names[i],
ASSET_X2_SETTINGS_HIGH_SCORE_NAME_LEN);
}
settings->coin_limit = 0;
settings->game_mode = ASSET_X2_SETTINGS_GAME_MODE_NORMAL;
settings->game_level = ASSET_X2_SETTINGS_LEVEL_NORMAL;
settings->game_stage_break = ASSET_X2_SETTINGS_STAGE_BREAK_2ND;
settings->language = ASSET_X2_SETTINGS_LANG_ENG;
settings->game_demo_sound = 1;
settings->game_show_help = 1;
settings->coin1_setting = 1;
settings->coin2_setting = 1;
settings->bookkeeping_coin1_total = 0;
settings->bookkeeping_coin2_total = 0;
settings->bookkeeping_service_total = 0;
return settings;
}
void asset_x2_settings_finalize(struct asset_x2_settings* settings)
{
/* Seems odd, but they just checksum the game settings part...*/
settings->adler32 = util_adler32_calc(1, (const uint8_t*) &settings->game_mode, 8);
}
void asset_x2_settings_unlock_all(struct asset_x2_settings* settings)
{
settings->unlock_canond_full = 1;
}
char* asset_x2_settings_to_string(const struct asset_x2_settings* settings)
{
// TODO
return "";
}
+82
View File
@@ -0,0 +1,82 @@
#ifndef ASSET_X2_SETTINGS_H
#define ASSET_X2_SETTINGS_H
#include <stdint.h>
#define ASSET_X2_SETTINGS_FILE_NAME "PIUEXCEED.INI"
#define ASSET_X2_SETTINGS_MAX_SONGS 139
#define ASSET_X2_SETTINGS_HIGH_SCORE_COUNT 20
#define ASSET_X2_SETTINGS_HIGH_SCORE_NAME_LEN 4
enum asset_x2_settings_language {
ASSET_X2_SETTINGS_LANG_KOR = 0,
ASSET_X2_SETTINGS_LANG_ENG = 1,
ASSET_X2_SETTINGS_LANG_SP = 2,
ASSET_X2_SETTINGS_LANG_CH = 3,
};
enum asset_x2_settings_game_mode {
ASSET_X2_SETTINGS_GAME_MODE_NORMAL = 0,
ASSET_X2_SETTINGS_GAME_MODE_EVENT = 1,
};
enum asset_x2_settings_level {
ASSET_X2_SETTINGS_LEVEL_EASY = 0,
ASSET_X2_SETTINGS_LEVEL_NORMAL = 1,
ASSET_X2_SETTINGS_LEVEL_HARD = 2,
};
enum asset_x2_settings_stage_break {
ASSET_X2_SETTINGS_STAGE_BREAK_OFF = 0,
ASSET_X2_SETTINGS_STAGE_BREAK_1ST = 1,
ASSET_X2_SETTINGS_STAGE_BREAK_2ND = 2,
ASSET_X2_SETTINGS_STAGE_BREAK_3RD = 3,
ASSET_X2_SETTINGS_STAGE_BREAK_4TH = 4,
};
struct asset_x2_settings {
/* Padding to fill up to 4k EEPROM legacy size */
uint8_t ff_pad[0x51B];
uint8_t unkn[10];
uint8_t unlock_canond_full;
uint32_t total_uptime;
uint8_t unkn69;
uint8_t song_blacklist[ASSET_X2_SETTINGS_MAX_SONGS];
uint8_t unkn3[0x75];
/* EX02 */
char ident[4];
uint32_t adler32;
uint16_t song_play_count[ASSET_X2_SETTINGS_MAX_SONGS];
uint16_t padding;
uint32_t high_score_scores[ASSET_X2_SETTINGS_HIGH_SCORE_COUNT];
/* 20 names a 4 chars for the default high score list */
char high_score_names[ASSET_X2_SETTINGS_HIGH_SCORE_COUNT][ASSET_X2_SETTINGS_HIGH_SCORE_NAME_LEN];
/* 0 none */
uint8_t coin_limit;
uint8_t game_mode;
uint8_t game_level;
uint8_t game_stage_break;
uint8_t language;
/* 0 off, 1 on */
uint8_t game_demo_sound;
/* 0 off, 1 on */
uint8_t game_show_help;
/* 0 freeplay */
uint8_t coin1_setting;
/* 0 freeplay */
uint8_t coin2_setting;
uint32_t bookkeeping_coin1_total;
uint32_t bookkeeping_coin2_total;
uint32_t bookkeeping_service_total;
} __attribute__((__packed__));
struct asset_x2_settings* asset_x2_settings_new(void);
// update checksum
void asset_x2_settings_finalize(struct asset_x2_settings* settings);
void asset_x2_settings_unlock_all(struct asset_x2_settings* settings);
char* asset_x2_settings_to_string(const struct asset_x2_settings* settings);
#endif
+62
View File
@@ -0,0 +1,62 @@
#include <string.h>
#include "asset/zero/lib/settings.h"
#include "util/adler32.h"
#include "util/mem.h"
struct asset_zero_settings* asset_zero_settings_new(void)
{
struct asset_zero_settings* settings;
settings = util_xmalloc(sizeof(struct asset_zero_settings));
memset(settings->ff_pad, 0xFF, sizeof(settings->ff_pad));
memcpy(settings->ident, "ZERO", 4);
/* Updated using finalize */
settings->adler32 = 0;
settings->total_uptime = 0;
settings->language = ASSET_ZERO_SETTINGS_LANG_ENG;
memset(settings->song_play_count, 0, sizeof(uint32_t) * ASSET_ZERO_SETTINGS_MAX_SONGS);
settings->game_option_default_station = ASSET_ZERO_SETTINGS_STATION_EASY;
settings->game_mode = ASSET_ZERO_SETTINGS_GAME_MODE_NORMAL;
settings->game_level = ASSET_ZERO_SETTINGS_LEVEL_NORMAL;
settings->game_stage_break = ASSET_ZERO_SETTINGS_STAGE_BREAK_2ND;
settings->game_demo_sound = 1;
settings->game_show_help = 1;
settings->game_mercy_ticket = 0;
settings->game_score_per_ticket = 0;
settings->coin_limit = 0;
settings->coin1_setting = 1;
settings->coin2_setting = 1;
memset(settings->mission_unlock_flags, 0, ASSET_ZERO_SETTINGS_NUM_UNLOCK_FLAGS);
settings->unkn2 = 0x2A;
settings->unkn3 = 0;
settings->bookkeeping_coin1_total = 0;
settings->bookkeeping_coin2_total = 0;
settings->bookkeeping_service_total = 0;
settings->bookkeeping_ticket_1 = 0;
settings->bookkeeping_ticket_2 = 0;
settings->region = 0xFF;
memset(settings->censor_table, 0, ASSET_ZERO_SETTINGS_MAX_SONGS);
return settings;
}
void asset_zero_settings_finalize(struct asset_zero_settings* settings)
{
/* Seems odd, but they just checksum the game settings part...*/
settings->adler32 = util_adler32_calc(1, (const uint8_t*) &settings->game_mode, 8);
}
void asset_zero_settings_unlock_all(struct asset_zero_settings* settings)
{
/* Mark all missions cleared to unlock all missions and content locked by some missions */
memset(settings->mission_unlock_flags, 1, ASSET_ZERO_SETTINGS_NUM_UNLOCK_FLAGS);
}
char* asset_zero_settings_to_string(const struct asset_zero_settings* settings)
{
// TODO
return "";
}
+95
View File
@@ -0,0 +1,95 @@
#ifndef ASSET_ZERO_SETTINGS_H
#define ASSET_ZERO_SETTINGS_H
#include <stdint.h>
#define ASSET_ZERO_SETTINGS_FILE_NAME "PIUZERO.INI"
#define ASSET_ZERO_SETTINGS_MAX_SONGS 200
#define ASSET_ZERO_SETTINGS_NUM_UNLOCK_FLAGS 59
enum asset_zero_settings_language {
ASSET_ZERO_SETTINGS_LANG_KOR = 0,
ASSET_ZERO_SETTINGS_LANG_ENG = 1,
ASSET_ZERO_SETTINGS_LANG_SP = 2,
ASSET_ZERO_SETTINGS_LANG_CH = 3,
};
enum asset_zero_settings_station {
ASSET_ZERO_SETTINGS_STATION_EASY = 0,
ASSET_ZERO_SETTINGS_STATION_ARCADE = 1,
ASSET_ZERO_SETTINGS_STATION_REMIX = 2,
};
enum asset_zero_settings_game_mode {
ASSET_ZERO_SETTINGS_GAME_MODE_NORMAL = 0,
ASSET_ZERO_SETTINGS_GAME_MODE_EVENT = 1,
};
enum asset_zero_settings_level {
ASSET_ZERO_SETTINGS_LEVEL_EASY = 0,
ASSET_ZERO_SETTINGS_LEVEL_NORMAL = 1,
ASSET_ZERO_SETTINGS_LEVEL_HARD = 2,
};
enum asset_zero_settings_stage_break {
ASSET_ZERO_SETTINGS_STAGE_BREAK_OFF = 0,
ASSET_ZERO_SETTINGS_STAGE_BREAK_1ST = 1,
ASSET_ZERO_SETTINGS_STAGE_BREAK_2ND = 2,
ASSET_ZERO_SETTINGS_STAGE_BREAK_3RD = 3,
ASSET_ZERO_SETTINGS_STAGE_BREAK_4TH = 4,
};
struct asset_zero_settings {
/* Padding to fill up to 4k EEPROM legacy size */
uint8_t ff_pad[0xBA4];
/* ZERO */
char ident[4];
uint32_t adler32;
uint32_t total_uptime;
uint8_t language;
uint32_t song_play_count[ASSET_ZERO_SETTINGS_MAX_SONGS];
uint32_t game_option_default_station;
uint8_t game_mode;
uint8_t game_level;
uint8_t game_stage_break;
/* 0 off, 1 on */
uint8_t game_demo_sound;
/* 0 off, 1 on */
uint8_t game_show_help;
/* 0 off, 1 on */
uint8_t game_mercy_ticket;
/* 0 off, 1 - 6 for score */
uint8_t game_score_per_ticket;
/* 0 none */
uint8_t coin_limit; // 0 none
/* 0 freeplay */
uint8_t coin1_setting;
/* 0 freeplay */
uint8_t coin2_setting;
/* Unlock flags for missions (total of 30) */
uint8_t mission_unlock_flags[ASSET_ZERO_SETTINGS_NUM_UNLOCK_FLAGS];
/* Default value 0x2A */
uint32_t unkn2;
uint32_t unkn3;
uint32_t bookkeeping_coin1_total;
uint32_t bookkeeping_coin2_total;
uint32_t bookkeeping_service_total;
uint32_t bookkeeping_ticket_1;
uint32_t bookkeeping_ticket_2;
/* FF -> not used, censoring globally disabled. unused feature though */
uint8_t region;
/* 0 = censoring of song off */
uint8_t censor_table[ASSET_ZERO_SETTINGS_MAX_SONGS];
} __attribute__((__packed__));
struct asset_zero_settings* asset_zero_settings_new(void);
// update checksum
void asset_zero_settings_finalize(struct asset_zero_settings* settings);
// sets all missions cleared to unlock special stage songs and other missions
void asset_zero_settings_unlock_all(struct asset_zero_settings* settings);
char* asset_zero_settings_to_string(const struct asset_zero_settings* settings);
#endif