mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
05f92b6422
* libs: fix CP932 iconv conversion on Windows and macOS
SDL3's SDL_SYSTEM_ICONV build option defaults off on Windows and macOS,
so SDL falls back to its own bundled iconv there, which only implements
ASCII/Latin-1/UTF/UCS. SDL_iconv_open("UTF-8", "CP932") always failed as
a result, crashing games that convert Shift-JIS strings via
sceCesMbcsStrToUtf8Str (e.g. save-data dialogs).
Add a small CesIconv shim selected at compile time: Windows converts one
character at a time via the Win32 codepage-932 APIs, replicating
SDL_iconv's chunked semantics and error codes; macOS calls real POSIX
iconv directly, since libiconv is always present there and does support
CP932, unlike SDL's fallback. Linux is untouched, since SDL already
links real system iconv by default there.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* libs: fix SaveDataDialog infinite loop on save
SaveDataDialogOpen() jumped straight from SAVE_STATUS_INITIALIZED (1) to
SAVE_STATUS_FINISHED (3), skipping SAVE_STATUS_RUNNING (2) entirely. At
least one title's save-write dialog stage only accepts a FINISHED status
after observing a RUNNING poll first; without it, the title assumed the
Open() request never took effect and re-issued it forever, hanging the
save flow (this only became visible once the CP932 crash above stopped
masking it).
Open() now starts the dialog as RUNNING. UpdateStatus() advances the
state machine, reporting RUNNING once before flipping to FINISHED for
subsequent polls; GetStatus() is untouched, staying a pure peek with no
side effects, matching the real SDK's pump-vs-peek split between the two
calls.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Add iconv linking to macOS
* Fix missing macOS iconv link for tests
* Fix CP932 edge cases and save dialog status consistency
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: nmzik <Nmzik@mail.ru>
167 lines
5.3 KiB
C++
167 lines
5.3 KiB
C++
#include "libs/dialog.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
namespace Loader::Timer {
|
|
double GetTimeMs() {
|
|
return 0.0;
|
|
}
|
|
} // namespace Loader::Timer
|
|
|
|
namespace {
|
|
namespace Save = Libs::Dialog::SaveDataDialog;
|
|
|
|
struct BaseParam {
|
|
uint64_t size;
|
|
uint8_t reserved[36];
|
|
uint32_t magic;
|
|
};
|
|
|
|
struct Items {
|
|
int32_t user_id;
|
|
uint32_t pad0;
|
|
const void* title_id;
|
|
const void* dir_names;
|
|
uint32_t dir_names_num;
|
|
uint32_t pad1;
|
|
const void* new_item;
|
|
int32_t focus_pos;
|
|
uint32_t pad2;
|
|
const void* focus_dir_name;
|
|
int32_t item_style;
|
|
uint8_t reserved[36];
|
|
};
|
|
|
|
struct UserMessage {
|
|
int32_t button_type;
|
|
int32_t message_type;
|
|
const char* message;
|
|
uint8_t reserved[32];
|
|
};
|
|
|
|
struct Param {
|
|
BaseParam base;
|
|
int32_t size;
|
|
int32_t mode;
|
|
int32_t display_type;
|
|
uint32_t pad0;
|
|
void* animation;
|
|
Items* items;
|
|
UserMessage* user_message;
|
|
void* system_message;
|
|
void* error_code;
|
|
void* progress_bar;
|
|
void* user_data;
|
|
void* option;
|
|
void* wizard;
|
|
uint8_t reserved[16];
|
|
};
|
|
|
|
static_assert(sizeof(BaseParam) == 48);
|
|
static_assert(sizeof(Items) == 96);
|
|
static_assert(sizeof(Param) == 152);
|
|
static_assert(offsetof(Param, items) == 72);
|
|
static_assert(offsetof(Param, user_data) == 112);
|
|
|
|
bool CheckLatestStatus(const char* stage) {
|
|
const int updated = Save::SaveDataDialogUpdateStatus();
|
|
const int current = Save::SaveDataDialogGetStatus();
|
|
if (updated != current) {
|
|
std::fprintf(stderr,
|
|
"SaveDataDialogTests: %s: UpdateStatus returned %d but the current "
|
|
"status is %d\n",
|
|
stage, updated, current);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CheckStatus(int actual, int expected, const char* stage) {
|
|
if (actual != expected) {
|
|
std::fprintf(stderr, "SaveDataDialogTests: %s: expected %d, got %d\n", stage, expected,
|
|
actual);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool TestRunningThenFinished(Param& param) {
|
|
bool passed = CheckStatus(Save::SaveDataDialogOpen(¶m), 0, "Open");
|
|
for (int poll = 0; poll < 3; ++poll) {
|
|
passed =
|
|
CheckStatus(Save::SaveDataDialogGetStatus(), 2, "peek before first update") && passed;
|
|
}
|
|
passed = CheckStatus(Save::SaveDataDialogUpdateStatus(), 2, "first update") && passed;
|
|
for (int poll = 0; poll < 3; ++poll) {
|
|
passed =
|
|
CheckStatus(Save::SaveDataDialogGetStatus(), 2, "peek after first update") && passed;
|
|
}
|
|
passed = CheckStatus(Save::SaveDataDialogUpdateStatus(), 3, "second update") && passed;
|
|
passed = CheckStatus(Save::SaveDataDialogGetStatus(), 3, "finished peek") && passed;
|
|
passed = CheckStatus(Save::SaveDataDialogUpdateStatus(), 3, "finished update") && passed;
|
|
return passed;
|
|
}
|
|
|
|
bool TestCloseAndReset(Param& param) {
|
|
bool passed = CheckStatus(Save::SaveDataDialogOpen(¶m), 0, "Open before Close");
|
|
passed =
|
|
CheckStatus(Save::SaveDataDialogClose(nullptr), 0, "Close before first poll") && passed;
|
|
passed = CheckStatus(Save::SaveDataDialogUpdateStatus(), 3, "poll after Close") && passed;
|
|
passed = CheckStatus(Save::SaveDataDialogGetStatus(), 3, "peek after Close") && passed;
|
|
passed = TestRunningThenFinished(param) && passed;
|
|
|
|
passed = CheckStatus(Save::SaveDataDialogOpen(¶m), 0, "Open before Terminate") && passed;
|
|
passed = CheckStatus(Save::SaveDataDialogUpdateStatus(), 2, "poll before Terminate") && passed;
|
|
passed = CheckStatus(Save::SaveDataDialogTerminate(), 0, "Terminate while running") && passed;
|
|
passed = CheckStatus(Save::SaveDataDialogGetStatus(), 0, "peek after Terminate") && passed;
|
|
passed = CheckStatus(Save::SaveDataDialogUpdateStatus(), 0, "poll after Terminate") && passed;
|
|
passed = CheckStatus(Save::SaveDataDialogInitialize(), 0, "reinitialize") && passed;
|
|
passed = CheckStatus(Save::SaveDataDialogUpdateStatus(), 1, "initialized update") && passed;
|
|
passed = TestRunningThenFinished(param) && passed;
|
|
return passed;
|
|
}
|
|
} // namespace
|
|
|
|
int main(int argc, char** argv) {
|
|
const bool consistency_only = argc == 2 && std::strcmp(argv[1], "--consistency-only") == 0;
|
|
if (argc != 1 && !consistency_only) {
|
|
std::fprintf(stderr, "usage: SaveDataDialogTests [--consistency-only]\n");
|
|
return 2;
|
|
}
|
|
Items items {};
|
|
items.user_id = 1000;
|
|
items.dir_names_num = 1; // No save-data information is displayed with a null dir_names.
|
|
UserMessage message {};
|
|
message.message = "Status polling";
|
|
Param param {};
|
|
param.base.size = sizeof(BaseParam);
|
|
param.base.magic =
|
|
static_cast<uint32_t>(0xc0d1a109u + reinterpret_cast<uintptr_t>(¶m.base));
|
|
param.size = sizeof(Param);
|
|
param.mode = 2; // USER_MSG
|
|
param.display_type = 1; // SAVE
|
|
param.items = &items;
|
|
param.user_message = &message;
|
|
|
|
if (Libs::Dialog::CommonDialog::CommonDialogInitialize() != 0 ||
|
|
Save::SaveDataDialogInitialize() != 0) {
|
|
return 2;
|
|
}
|
|
bool passed = CheckLatestStatus("initialized");
|
|
if (Save::SaveDataDialogOpen(¶m) != 0) {
|
|
return 2;
|
|
}
|
|
passed = CheckLatestStatus("first poll after Open") && passed;
|
|
passed = CheckLatestStatus("second poll after Open") && passed;
|
|
if (!consistency_only) {
|
|
passed = TestRunningThenFinished(param) && passed;
|
|
passed = TestCloseAndReset(param) && passed;
|
|
}
|
|
Save::SaveDataDialogTerminate();
|
|
passed = CheckLatestStatus("terminated") && passed;
|
|
return passed ? 0 : 1;
|
|
}
|