mirror of
https://github.com/MonsterDruide1/OdysseyDecomp
synced 2026-04-23 09:04:21 +00:00
Util: Implement ScenePrepoFunction (#1001)
This commit is contained in:
parent
e95a370468
commit
976594c87c
|
|
@ -158496,87 +158496,87 @@ Util/ScenePrepoFunction.o:
|
||||||
- offset: 0x574094
|
- offset: 0x574094
|
||||||
size: 288
|
size: 288
|
||||||
label: _ZN2rs32trySavePrepoCompleteMainScenarioEiilll
|
label: _ZN2rs32trySavePrepoCompleteMainScenarioEiilll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x5741b4
|
- offset: 0x5741b4
|
||||||
size: 380
|
size: 380
|
||||||
label: _ZN2rs22trySavePrepoStartStageEPKcRKN4sead7Vector3IfEElll
|
label: _ZN2rs22trySavePrepoStartStageEPKcRKN4sead7Vector3IfEElll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x574330
|
- offset: 0x574330
|
||||||
size: 380
|
size: 380
|
||||||
label: _ZN2rs21trySavePrepoExitStageEPKcRKN4sead7Vector3IfEElll
|
label: _ZN2rs21trySavePrepoExitStageEPKcRKN4sead7Vector3IfEElll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x5744ac
|
- offset: 0x5744ac
|
||||||
size: 380
|
size: 380
|
||||||
label: _ZN2rs21trySavePrepoMissEventEPKcRKN4sead7Vector3IfEElll
|
label: _ZN2rs21trySavePrepoMissEventEPKcRKN4sead7Vector3IfEElll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x574628
|
- offset: 0x574628
|
||||||
size: 364
|
size: 364
|
||||||
label: _ZN2rs25trySavePrepoShineGetEventEPKciiilll
|
label: _ZN2rs25trySavePrepoShineGetEventEPKciiilll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x574794
|
- offset: 0x574794
|
||||||
size: 308
|
size: 308
|
||||||
label: _ZN2rs30trySavePrepoReceiveAchievementEPKcilll
|
label: _ZN2rs30trySavePrepoReceiveAchievementEPKcilll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x5748c8
|
- offset: 0x5748c8
|
||||||
size: 1216
|
size: 1216
|
||||||
label: _ZN2rs31trySavePrepoAchievementProgressE22GameDataHolderAccessorlll
|
label: _ZN2rs31trySavePrepoAchievementProgressE22GameDataHolderAccessorlll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x574d88
|
- offset: 0x574d88
|
||||||
size: 324
|
size: 324
|
||||||
label: _ZN2rs23trySavePrepoGetCapEventEPKcilll
|
label: _ZN2rs23trySavePrepoGetCapEventEPKcilll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x574ecc
|
- offset: 0x574ecc
|
||||||
size: 328
|
size: 328
|
||||||
label: _ZN2rs25trySavePrepoGetClothEventEPKcilll
|
label: _ZN2rs25trySavePrepoGetClothEventEPKcilll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x575014
|
- offset: 0x575014
|
||||||
size: 328
|
size: 328
|
||||||
label: _ZN2rs24trySavePrepoGetGiftEventEPKcilll
|
label: _ZN2rs24trySavePrepoGetGiftEventEPKcilll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x57515c
|
- offset: 0x57515c
|
||||||
size: 328
|
size: 328
|
||||||
label: _ZN2rs27trySavePrepoGetStickerEventEPKcilll
|
label: _ZN2rs27trySavePrepoGetStickerEventEPKcilll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x5752a4
|
- offset: 0x5752a4
|
||||||
size: 292
|
size: 292
|
||||||
label: _ZN2rs26trySavePrepoChangeCapEventEPKclll
|
label: _ZN2rs26trySavePrepoChangeCapEventEPKclll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x5753c8
|
- offset: 0x5753c8
|
||||||
size: 296
|
size: 296
|
||||||
label: _ZN2rs28trySavePrepoChangeClothEventEPKclll
|
label: _ZN2rs28trySavePrepoChangeClothEventEPKclll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x5754f0
|
- offset: 0x5754f0
|
||||||
size: 308
|
size: 308
|
||||||
label: _ZN2rs26trySavePrepoFirstHackEventEPKcilll
|
label: _ZN2rs26trySavePrepoFirstHackEventEPKcilll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x575624
|
- offset: 0x575624
|
||||||
size: 384
|
size: 384
|
||||||
label: _ZN2rs30trySavePrepoGetCollectBgmEventEPKcS1_billl
|
label: _ZN2rs30trySavePrepoGetCollectBgmEventEPKcS1_billl
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x5757a4
|
- offset: 0x5757a4
|
||||||
size: 504
|
size: 504
|
||||||
label: _ZN2rs25trySavePrepoSettingsStateEbPKcRK14GameConfigDatall
|
label: _ZN2rs25trySavePrepoSettingsStateEbPKcRK14GameConfigDatall
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x57599c
|
- offset: 0x57599c
|
||||||
size: 256
|
size: 256
|
||||||
label: _ZN2rs28trySavePrepoSeparatePlayModeEblll
|
label: _ZN2rs28trySavePrepoSeparatePlayModeEblll
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x575a9c
|
- offset: 0x575a9c
|
||||||
size: 4
|
size: 4
|
||||||
label: _ZN2rs5prepo17enableIsSavePrepoEv
|
label: _ZN2rs5prepo17enableIsSavePrepoEv
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x575aa0
|
- offset: 0x575aa0
|
||||||
size: 4
|
size: 4
|
||||||
label: _ZN2rs5prepo18disableIsSavePrepoEv
|
label: _ZN2rs5prepo18disableIsSavePrepoEv
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x575aa4
|
- offset: 0x575aa4
|
||||||
size: 68
|
size: 68
|
||||||
label: _ZN2rs5prepo18generateSaveDataIdEv
|
label: _ZN2rs5prepo18generateSaveDataIdEv
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
- offset: 0x575ae8
|
- offset: 0x575ae8
|
||||||
size: 12
|
size: 12
|
||||||
label: _ZN2rs5prepo17calcPrepoHashCodeEPKc
|
label: _ZN2rs5prepo17calcPrepoHashCodeEPKc
|
||||||
status: NotDecompiled
|
status: Matching
|
||||||
Util/StageInputFunction.o:
|
Util/StageInputFunction.o:
|
||||||
'.text':
|
'.text':
|
||||||
- offset: 0x575af4
|
- offset: 0x575af4
|
||||||
|
|
|
||||||
|
|
@ -5,37 +5,37 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace al {
|
namespace al {
|
||||||
s32 calcHashCode(const char* str) {
|
u32 calcHashCode(const char* str) {
|
||||||
if (str[0] == '\0')
|
if (str[0] == '\0')
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
s32 hashCode = 0;
|
u32 hashCode = 0;
|
||||||
for (s32 i = 0; str[i] != '\0'; i++)
|
for (s32 i = 0; str[i] != '\0'; i++)
|
||||||
hashCode = (hashCode * 0x1f) + str[i];
|
hashCode = (hashCode * 0x1f) + str[i];
|
||||||
|
|
||||||
return hashCode;
|
return hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 calcHashCodeLower(const char* str) {
|
u32 calcHashCodeLower(const char* str) {
|
||||||
s32 hashCode = 0;
|
u32 hashCode = 0;
|
||||||
for (s32 i = 0; str[i] != '\0'; i++)
|
for (s32 i = 0; str[i] != '\0'; i++)
|
||||||
hashCode = (hashCode * 0x1f) + tolower(str[i]);
|
hashCode = (hashCode * 0x1f) + tolower(str[i]);
|
||||||
|
|
||||||
return hashCode;
|
return hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 calcHashCodeFmt(const char* format, std::va_list argv) {
|
u32 calcHashCodeFmt(const char* format, std::va_list argv) {
|
||||||
char buf[0x100];
|
char buf[0x100];
|
||||||
vsnprintf(buf, 0x100, format, argv);
|
vsnprintf(buf, 0x100, format, argv);
|
||||||
|
|
||||||
return calcHashCode(buf);
|
return calcHashCode(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 calcHashCodeFmt(const char* format, ...) {
|
u32 calcHashCodeFmt(const char* format, ...) {
|
||||||
std::va_list argv;
|
std::va_list argv;
|
||||||
|
|
||||||
va_start(argv, format);
|
va_start(argv, format);
|
||||||
s32 result = calcHashCodeFmt(format, argv);
|
u32 result = calcHashCodeFmt(format, argv);
|
||||||
va_end(argv);
|
va_end(argv);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
|
||||||
namespace al {
|
namespace al {
|
||||||
s32 calcHashCode(const char* str);
|
u32 calcHashCode(const char* str);
|
||||||
s32 calcHashCodeLower(const char* str);
|
u32 calcHashCodeLower(const char* str);
|
||||||
s32 calcHashCodeFmt(const char* format, std::va_list argv);
|
u32 calcHashCodeFmt(const char* format, std::va_list argv);
|
||||||
s32 calcHashCodeFmt(const char* format, ...);
|
u32 calcHashCodeFmt(const char* format, ...);
|
||||||
const char* getBaseName(const char* name);
|
const char* getBaseName(const char* name);
|
||||||
} // namespace al
|
} // namespace al
|
||||||
|
|
|
||||||
460
src/Util/ScenePrepoFunction.cpp
Normal file
460
src/Util/ScenePrepoFunction.cpp
Normal file
|
|
@ -0,0 +1,460 @@
|
||||||
|
#include "Util/ScenePrepoFunction.h"
|
||||||
|
|
||||||
|
#include <nn/account.h>
|
||||||
|
#include <nn/prepo.h>
|
||||||
|
#include <nn/time.h>
|
||||||
|
|
||||||
|
#include "Library/Base/HashCodeUtil.h"
|
||||||
|
|
||||||
|
#include "System/GameConfigData.h"
|
||||||
|
#include "System/GameDataFunction.h"
|
||||||
|
#include "System/GameDataUtil.h"
|
||||||
|
#include "Util/AchievementUtil.h"
|
||||||
|
#include "Util/ClothUtil.h"
|
||||||
|
|
||||||
|
namespace rs {
|
||||||
|
static char buffer[0x2000];
|
||||||
|
|
||||||
|
bool trySavePrepoCompleteMainScenario(s32 worldId, s32 mainScenarioNo, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("complete_main_scenario_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(6));
|
||||||
|
|
||||||
|
playReport.Add("world_id", static_cast<s64>(worldId));
|
||||||
|
playReport.Add("main_scenario_no", static_cast<s64>(mainScenarioNo));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoStartStage(const char* stageName, const sead::Vector3f& position, s64 playTime,
|
||||||
|
s64 saveDataId, s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("stage_in_out_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// BUG: buffer too small (prepared for 8 entries, adds 9)
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(8));
|
||||||
|
|
||||||
|
playReport.Add("stage_name", static_cast<s64>(prepo::calcPrepoHashCode(stageName)));
|
||||||
|
playReport.Add("x", position.x);
|
||||||
|
playReport.Add("y", position.y);
|
||||||
|
playReport.Add("z", position.z);
|
||||||
|
playReport.Add("in_out_type", InOutType_Start);
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoExitStage(const char* stageName, const sead::Vector3f& position, s64 playTime,
|
||||||
|
s64 saveDataId, s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("stage_in_out_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// BUG: buffer too small (prepared for 8 entries, adds 9)
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(8));
|
||||||
|
|
||||||
|
playReport.Add("stage_name", static_cast<s64>(prepo::calcPrepoHashCode(stageName)));
|
||||||
|
playReport.Add("x", position.x);
|
||||||
|
playReport.Add("y", position.y);
|
||||||
|
playReport.Add("z", position.z);
|
||||||
|
playReport.Add("in_out_type", InOutType_Exit);
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoMissEvent(const char* stageName, const sead::Vector3f& position, s64 playTime,
|
||||||
|
s64 saveDataId, s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("stage_in_out_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// BUG: buffer too small (prepared for 8 entries, adds 9)
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(8));
|
||||||
|
|
||||||
|
playReport.Add("stage_name", static_cast<s64>(prepo::calcPrepoHashCode(stageName)));
|
||||||
|
playReport.Add("x", position.x);
|
||||||
|
playReport.Add("y", position.y);
|
||||||
|
playReport.Add("z", position.z);
|
||||||
|
playReport.Add("in_out_type", InOutType_Miss);
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoShineGetEvent(const char* stageName, s32 shineId, s32 totalShineNum,
|
||||||
|
s32 totalShopShineNum, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("shine_get_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(8));
|
||||||
|
|
||||||
|
playReport.Add("stage_name", static_cast<s64>(prepo::calcPrepoHashCode(stageName)));
|
||||||
|
playReport.Add("shine_id", static_cast<s64>(shineId));
|
||||||
|
playReport.Add("total_shine_num", static_cast<s64>(totalShineNum));
|
||||||
|
playReport.Add("total_shop_shine_num", static_cast<s64>(totalShopShineNum));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoReceiveAchievement(const char* achievementName, s32 receivedNum, s64 playTime,
|
||||||
|
s64 saveDataId, s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("receive_achievement_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(6));
|
||||||
|
|
||||||
|
playReport.Add("achievement_name", static_cast<s64>(prepo::calcPrepoHashCode(achievementName)));
|
||||||
|
playReport.Add("received_num", static_cast<s64>(receivedNum));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoAchievementProgress(GameDataHolderAccessor accessor, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("achievement_progress_state");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(38));
|
||||||
|
|
||||||
|
playReport.Add("shine_gather", static_cast<s64>(GameDataFunction::getTotalShineNum(accessor)));
|
||||||
|
playReport.Add("shine_collect_coin_shop",
|
||||||
|
static_cast<s64>(calcBuyItemNumForCoinCollectByWorld(accessor)));
|
||||||
|
playReport.Add("shine_shine_2d", static_cast<s64>(calcGetShineNumDot(accessor)));
|
||||||
|
playReport.Add("shine_treasure_box", static_cast<s64>(calcGetShineNumTreasureBox(accessor)));
|
||||||
|
playReport.Add("shine_music_note", static_cast<s64>(calcGetShineNumNoteObj(accessor)));
|
||||||
|
// typo: athretic / athletic
|
||||||
|
playReport.Add("shine_timer_athretic",
|
||||||
|
static_cast<s64>(calcGetShineNumTimerAthletic(accessor)));
|
||||||
|
playReport.Add("shine_captain_kinopio",
|
||||||
|
static_cast<s64>(calcGetShineNumKinopioBrigade(accessor)));
|
||||||
|
playReport.Add("shine_traveling_peach",
|
||||||
|
static_cast<s64>(calcGetShineNumWorldTravelingPeach(accessor)));
|
||||||
|
playReport.Add("shine_collect_animal_all",
|
||||||
|
static_cast<s64>(calcGetShineNumCollectAnimal(accessor)));
|
||||||
|
playReport.Add("shine_kuribo_girl", static_cast<s64>(calcGetShineNumKuriboGirl(accessor)));
|
||||||
|
playReport.Add("shine_jugem", static_cast<s64>(calcGetShineNumJugemFish(accessor)));
|
||||||
|
playReport.Add("shine_seed", static_cast<s64>(calcGetShineNumGrowPlant(accessor)));
|
||||||
|
playReport.Add("shine_rabbit", static_cast<s64>(calcGetShineNumRabbit(accessor)));
|
||||||
|
playReport.Add("shine_dig_point", static_cast<s64>(calcGetShineNumDigPoint(accessor)));
|
||||||
|
playReport.Add("shine_cap_hanger", static_cast<s64>(calcGetShineNumCapHanger(accessor)));
|
||||||
|
playReport.Add("shine_bird", static_cast<s64>(calcGetShineNumBird(accessor)));
|
||||||
|
playReport.Add("shine_costume_room", static_cast<s64>(calcGetShineNumCostumeRoom(accessor)));
|
||||||
|
playReport.Add("shine_cap_throttle", static_cast<s64>(calcGetShineNumSlot(accessor)));
|
||||||
|
playReport.Add("shine_hide_and_seek_cap_man",
|
||||||
|
static_cast<s64>(calcGetShineNumHideAndSeekCapMan(accessor)));
|
||||||
|
playReport.Add("shine_collect_bgm", static_cast<s64>(calcGetShineNumCollectedBgm(accessor)));
|
||||||
|
playReport.Add("shine_hint_photo", static_cast<s64>(calcGetShineNumHintPhoto(accessor)));
|
||||||
|
playReport.Add("mini_game_race_man", static_cast<s64>(calcGetShineNumRace(accessor)));
|
||||||
|
playReport.Add("mini_game_figure_walker",
|
||||||
|
static_cast<s64>(calcGetShineNumFigureWalking(accessor)));
|
||||||
|
playReport.Add("mini_game_sphinx_quiz", static_cast<s64>(calcSphinxQuizCompleteNum(accessor)));
|
||||||
|
playReport.Add("souvenir_count",
|
||||||
|
static_cast<s64>(calcHaveGiftNum(accessor) + calcHaveStickerNum(accessor)));
|
||||||
|
playReport.Add("capture_count", static_cast<s64>(calcHackObjNum(accessor)));
|
||||||
|
playReport.Add("costume_cap", static_cast<s64>(calcHaveCapNum(accessor)));
|
||||||
|
playReport.Add("costume_clothes", static_cast<s64>(calcHaveClothNum(accessor)));
|
||||||
|
playReport.Add("other_moon_stone_all", static_cast<s64>(calcUnlockMoonRockNum(accessor)));
|
||||||
|
playReport.Add("other_world_warp_hole_all",
|
||||||
|
static_cast<s64>(calcWorldWarpHoleThroughNum(accessor)));
|
||||||
|
playReport.Add("other_check_point", static_cast<s64>(calcGetCheckpointNum(accessor)));
|
||||||
|
playReport.Add("other_coin", static_cast<s64>(getTotalCoinNum(accessor)));
|
||||||
|
playReport.Add("other_jump", static_cast<s64>(getPlayerJumpCount(accessor)));
|
||||||
|
playReport.Add("other_cap_throw", static_cast<s64>(getPlayerThrowCapCount(accessor)));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoGetCapEvent(const char* itemName, s32 totalNum, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("collect_item_get_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(7));
|
||||||
|
|
||||||
|
playReport.Add("category", ItemCategoryType_Cap);
|
||||||
|
playReport.Add("item_name", static_cast<s64>(prepo::calcPrepoHashCode(itemName)));
|
||||||
|
playReport.Add("total_num", static_cast<s64>(totalNum));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoGetClothEvent(const char* itemName, s32 totalNum, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("collect_item_get_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(7));
|
||||||
|
|
||||||
|
playReport.Add("category", ItemCategoryType_Cloth);
|
||||||
|
playReport.Add("item_name", static_cast<s64>(prepo::calcPrepoHashCode(itemName)));
|
||||||
|
playReport.Add("total_num", static_cast<s64>(totalNum));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoGetGiftEvent(const char* itemName, s32 totalNum, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("collect_item_get_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(7));
|
||||||
|
|
||||||
|
playReport.Add("category", ItemCategoryType_Gift);
|
||||||
|
playReport.Add("item_name", static_cast<s64>(prepo::calcPrepoHashCode(itemName)));
|
||||||
|
playReport.Add("total_num", static_cast<s64>(totalNum));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoGetStickerEvent(const char* itemName, s32 totalNum, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("collect_item_get_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(7));
|
||||||
|
|
||||||
|
playReport.Add("category", ItemCategoryType_Sticker);
|
||||||
|
playReport.Add("item_name", static_cast<s64>(prepo::calcPrepoHashCode(itemName)));
|
||||||
|
playReport.Add("total_num", static_cast<s64>(totalNum));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoChangeCapEvent(const char* itemName, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("cloth_change_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(6));
|
||||||
|
|
||||||
|
playReport.Add("category", ItemCategoryType_Cap);
|
||||||
|
playReport.Add("item_name", static_cast<s64>(prepo::calcPrepoHashCode(itemName)));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoChangeClothEvent(const char* itemName, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("cloth_change_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(6));
|
||||||
|
|
||||||
|
playReport.Add("category", ItemCategoryType_Cloth);
|
||||||
|
playReport.Add("item_name", static_cast<s64>(prepo::calcPrepoHashCode(itemName)));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoFirstHackEvent(const char* hackObjName, s32 totalHackedObjNum, s64 playTime,
|
||||||
|
s64 saveDataId, s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("first_hack_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(6));
|
||||||
|
|
||||||
|
playReport.Add("hack_obj_name", static_cast<s64>(prepo::calcPrepoHashCode(hackObjName)));
|
||||||
|
playReport.Add("total_hacked_obj_num", static_cast<s64>(totalHackedObjNum));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoGetCollectBgmEvent(const char* bgmResourceName, const char* bgmSituationName,
|
||||||
|
bool isBailout, s32 collectedBgmNum, s64 playTime,
|
||||||
|
s64 saveDataId, s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("get_collect_bgm_event");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(8));
|
||||||
|
|
||||||
|
playReport.Add("bgm_resource_name",
|
||||||
|
static_cast<s64>(prepo::calcPrepoHashCode(bgmResourceName)));
|
||||||
|
playReport.Add("bgm_situation_name",
|
||||||
|
static_cast<s64>(prepo::calcPrepoHashCode(bgmSituationName)));
|
||||||
|
playReport.Add("is_bailout", static_cast<s64>(isBailout));
|
||||||
|
playReport.Add("collected_bgm_num", static_cast<s64>(collectedBgmNum));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoSettingsState(bool isKidsMode, const char* language,
|
||||||
|
const GameConfigData& configData, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("settings_state");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// NOTE: buffer too large (prepared for 14 entries, adds 12)
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(14));
|
||||||
|
|
||||||
|
playReport.Add("is_kids_mode", static_cast<s64>(isKidsMode));
|
||||||
|
playReport.Add("language", static_cast<s64>(prepo::calcPrepoHashCode(language)));
|
||||||
|
playReport.Add("camera_stick_sensitivity_level",
|
||||||
|
static_cast<s64>(configData.getCameraStickSensitivityLevel()));
|
||||||
|
bool isCameraReverseInputH = configData.isCameraReverseInputH();
|
||||||
|
bool isCameraReverseInputV = configData.isCameraReverseInputV();
|
||||||
|
playReport.Add("camera_reverse_input",
|
||||||
|
static_cast<s64>((isCameraReverseInputH << 1) | isCameraReverseInputV));
|
||||||
|
playReport.Add("is_valid_camera_gyro", static_cast<s64>(configData.isValidCameraGyro()));
|
||||||
|
playReport.Add("camera_gyro_sensitivity_level",
|
||||||
|
static_cast<s64>(configData.getCameraGyroSensitivityLevel()));
|
||||||
|
playReport.Add("is_use_open_list_additional_button",
|
||||||
|
static_cast<s64>(configData.isUseOpenListAdditionalButton()));
|
||||||
|
playReport.Add("is_valid_pad_rumble", static_cast<s64>(configData.isValidPadRumble()));
|
||||||
|
playReport.Add("pad_rumble_level", static_cast<s64>(configData.getPadRumbleLevel()));
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trySavePrepoSeparatePlayMode(bool isSeparatePlayMode, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime) {
|
||||||
|
nn::account::Uid lastOpenedUser;
|
||||||
|
nn::prepo::PlayReport playReport = nn::prepo::PlayReport("separate_play_mode_state");
|
||||||
|
|
||||||
|
if (nn::account::GetLastOpenedUser(&lastOpenedUser).IsFailure() || !lastOpenedUser.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
playReport.SetBuffer(&buffer, nn::prepo::PlayReport::CalcBufferSize(5));
|
||||||
|
|
||||||
|
playReport.Add("is_separate_play_mode", static_cast<s64>(isSeparatePlayMode));
|
||||||
|
playReport.Add("play_time", playTime);
|
||||||
|
playReport.Add("across_play_time", acrossPlayTime);
|
||||||
|
playReport.Add("save_data_id", saveDataId);
|
||||||
|
playReport.Add("app_version", 1l);
|
||||||
|
|
||||||
|
return playReport.Save(lastOpenedUser).IsSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace prepo {
|
||||||
|
void enableIsSavePrepo() {}
|
||||||
|
|
||||||
|
void disableIsSavePrepo() {}
|
||||||
|
|
||||||
|
s64 generateSaveDataId() {
|
||||||
|
if (!nn::time::IsInitialized() && nn::time::Initialize().IsFailure())
|
||||||
|
return -1l;
|
||||||
|
|
||||||
|
nn::time::PosixTime time;
|
||||||
|
if (nn::time::StandardUserSystemClock::GetCurrentTime(&time).IsFailure())
|
||||||
|
return -1l;
|
||||||
|
|
||||||
|
return time.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 calcPrepoHashCode(const char* str) {
|
||||||
|
if (!str)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return al::calcHashCode(str);
|
||||||
|
}
|
||||||
|
} // namespace prepo
|
||||||
|
} // namespace rs
|
||||||
|
|
@ -3,32 +3,62 @@
|
||||||
#include <basis/seadTypes.h>
|
#include <basis/seadTypes.h>
|
||||||
#include <math/seadVector.h>
|
#include <math/seadVector.h>
|
||||||
|
|
||||||
class GameDataHolderAccessor;
|
#include "System/GameDataHolderAccessor.h"
|
||||||
|
|
||||||
class GameConfigData;
|
class GameConfigData;
|
||||||
|
|
||||||
namespace rs {
|
namespace rs {
|
||||||
bool trySavePrepoCompleteMainScenario(s32, s32, s64, s64, s64);
|
enum InOutType : s64 { InOutType_Start, InOutType_Exit, InOutType_Miss };
|
||||||
bool trySavePrepoStartStage(const char*, const sead::Vector3f&, s64, s64, s64);
|
|
||||||
bool trySavePrepoExitStage(const char*, const sead::Vector3f&, s64, s64, s64);
|
enum ItemCategoryType : s64 {
|
||||||
bool trySavePrepoMissEvent(const char*, const sead::Vector3f&, s64, s64, s64);
|
ItemCategoryType_Cap,
|
||||||
bool trySavePrepoShineGetEvent(const char*, s32, s32, s32, s64, s64, s64);
|
ItemCategoryType_Cloth,
|
||||||
bool trySavePrepoReceiveAchievement(const char* s32, s64, s64, s64);
|
ItemCategoryType_Gift,
|
||||||
bool trySavePrepoAchievementProgress(GameDataHolderAccessor, s64, s64, s64);
|
ItemCategoryType_Sticker
|
||||||
bool trySavePrepoGetCapEvent(const char*, s32, s64, s64, s64);
|
};
|
||||||
bool trySavePrepoGetClothEvent(const char*, s32, s64, s64, s64);
|
|
||||||
bool trySavePrepoGetGiftEvent(const char*, s32, s64, s64, s64);
|
bool trySavePrepoCompleteMainScenario(s32 worldId, s32 mainScenarioNo, s64 playTime, s64 saveDataId,
|
||||||
bool trySavePrepoGetStickerEvent(const char*, s32, s64, s64, s64);
|
s64 acrossPlayTime);
|
||||||
bool trySavePrepoChangeCapEvent(const char*, s64, s64, s64);
|
bool trySavePrepoStartStage(const char* stageName, const sead::Vector3f& position, s64 playTime,
|
||||||
bool trySavePrepoChangeClothEvent(const char*, s64, s64, s64);
|
s64 saveDataId, s64 acrossPlayTime);
|
||||||
bool trySavePrepoFirstHackEvent(const char*, s32, s64, s64, s64);
|
bool trySavePrepoExitStage(const char* stageName, const sead::Vector3f& position, s64 playTime,
|
||||||
bool trySavePrepoGetCollectBgmEvent(const char*, const char*, bool, s32, s64, s64, s64);
|
s64 saveDataId, s64 acrossPlayTime);
|
||||||
bool trySavePrepoSettingsState(bool, const char*, const GameConfigData&, s64, s64);
|
bool trySavePrepoMissEvent(const char* stageName, const sead::Vector3f& position, s64 playTime,
|
||||||
bool trySavePrepoSeparatePlayMode(bool, s64, s64, s64);
|
s64 saveDataId, s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoShineGetEvent(const char* stageName, s32 shineId, s32 totalShineNum,
|
||||||
|
s32 totalShopShineNum, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoReceiveAchievement(const char* achievementName, s32 receivedNum, s64 playTime,
|
||||||
|
s64 saveDataId, s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoAchievementProgress(GameDataHolderAccessor accessor, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoGetCapEvent(const char* itemName, s32 totalNum, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoGetClothEvent(const char* itemName, s32 totalNum, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoGetGiftEvent(const char* itemName, s32 totalNum, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoGetStickerEvent(const char* itemName, s32 totalNum, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoChangeCapEvent(const char* itemName, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoChangeClothEvent(const char* itemName, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoFirstHackEvent(const char* hackObjName, s32 totalHackedObjNum, s64 playTime,
|
||||||
|
s64 saveDataId, s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoGetCollectBgmEvent(const char* bgmResourceName, const char* bgmSituationName,
|
||||||
|
bool isBailout, s32 collectedBgmNum, s64 playTime,
|
||||||
|
s64 saveDataId, s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoSettingsState(bool isKidsMode, const char* language,
|
||||||
|
const GameConfigData& configData, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime);
|
||||||
|
bool trySavePrepoSeparatePlayMode(bool isSeparatePlayMode, s64 playTime, s64 saveDataId,
|
||||||
|
s64 acrossPlayTime);
|
||||||
|
|
||||||
namespace prepo {
|
namespace prepo {
|
||||||
void enableIsSavePrepo();
|
void enableIsSavePrepo();
|
||||||
void disableIsSavePrepo();
|
void disableIsSavePrepo();
|
||||||
s64 generateSaveDataId();
|
s64 generateSaveDataId();
|
||||||
s32 calcPrepoHashCode(const char*);
|
u32 calcPrepoHashCode(const char* str);
|
||||||
} // namespace prepo
|
} // namespace prepo
|
||||||
} // namespace rs
|
} // namespace rs
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue