mirror of
https://github.com/pumpitupdev/pumptools.git
synced 2026-10-12 00:58:14 +03:00
Version 1.08 with public source code release.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 "";
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 "";
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 "";
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user