Skip to content

Commit

Permalink
增加角色参数编辑器
Browse files Browse the repository at this point in the history
  • Loading branch information
chinosk6 committed Jan 26, 2024
1 parent 2e3c2a0 commit 1443629
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 17 deletions.
131 changes: 131 additions & 0 deletions src/hook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ std::function<void()> g_on_close;
std::function<void()> on_hotKey_0;
bool needPrintStack = false;
std::vector<std::pair<std::pair<int, int>, int>> replaceDressResIds{};
std::map<std::string, CharaParam_t> charaParam{};
CharaParam_t baseParam(0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
std::vector<std::function<bool()>> mainThreadTasks{}; // 返回 true,执行后移除列表;返回 false,执行后不移除

template<typename T, typename TF>
void convertPtrType(T* cvtTarget, TF func_ptr) {
Expand All @@ -32,9 +35,30 @@ void convertPtrType(T* cvtTarget, TF func_ptr) {

bool exd = false;
void* SetResolution_orig;
void* AssembleCharacter_ApplyParam_orig;
Il2CppString* (*environment_get_stacktrace)();


void CharaParam_t::Apply() {
const auto ApplyParamFunc = reinterpret_cast<void (*)(void*, float, float, float, float, float)>(
AssembleCharacter_ApplyParam_orig
);
auto currObjPtr = getObjPtr();
if (currObjPtr) {
ApplyParamFunc(currObjPtr, height + baseParam.height, bust + baseParam.bust,
head + baseParam.head, arm + baseParam.arm, hand + baseParam.hand);
}
}

void CharaParam_t::ApplyOnMainThread() {
mainThreadTasks.push_back([this](){
this->Apply();
if (!g_enable_chara_param_edit) return true;
return !(this->gui_real_time_apply || baseParam.gui_real_time_apply);
});
}


namespace Utils {
template <typename KT = void*, typename VT = void*>
class CSDictEditor {
Expand Down Expand Up @@ -828,6 +852,62 @@ namespace

}

void AssembleCharacter_ApplyParam_hook(void* mdl, float height, float bust, float head, float arm, float hand) {
if (g_enable_chara_param_edit) {
static auto get_ObjectName = reinterpret_cast<Il2CppString * (*)(void*)>(il2cpp_resolve_icall("UnityEngine.Object::GetName(UnityEngine.Object)"));
const auto objNameIlStr = get_ObjectName(mdl);
const std::string objName = objNameIlStr ? utility::conversions::to_utf8string(std::wstring(objNameIlStr->start_char)) : std::format("Unnamed Obj {:p}", mdl);
std::string showObjName;
if (objName.starts_with("m_ALL_")) {
const auto nextFlagPos = objName.find_first_of(L'_', 6);
if (nextFlagPos > 6) {
showObjName = objName.substr(0, nextFlagPos);
}
else {
showObjName = objName;
}
}
else {
showObjName = objName;
}
if (auto it = charaParam.find(showObjName); it != charaParam.end()) {
it->second.SetObjPtr(mdl);
it->second.UpdateParam(&height, &bust, &head, &arm, &hand);
return it->second.Apply();
}
else {
charaParam.emplace(showObjName, CharaParam_t(height, bust, head, arm, hand, mdl));
}
}
return reinterpret_cast<decltype(AssembleCharacter_ApplyParam_hook)*>(AssembleCharacter_ApplyParam_orig)(mdl, height, bust, head, arm, hand);
}

void* MainThreadDispatcher_LateUpdate_orig;
void MainThreadDispatcher_LateUpdate_hook(void* _this, void* method) {
try {
auto it = mainThreadTasks.begin();
while (it != mainThreadTasks.end()) {
try {
bool result = (*it)();
if (result) {
it = mainThreadTasks.erase(it);
}
else {
++it;
}
}
catch (std::exception& e) {
printf("MainThreadDispatcher Tasks Error: %s\n", e.what());
it = mainThreadTasks.erase(it);
}
}
}
catch (std::exception& ex) {
printf("MainThreadDispatcher Error: %s\n", ex.what());
}
return reinterpret_cast<decltype(MainThreadDispatcher_LateUpdate_hook)*>(MainThreadDispatcher_LateUpdate_orig)(_this, method);
}

void checkAndAddCostume(void* _this, int key, void* value, MethodInfo* method) {
static auto CostumeStatus_klass = il2cpp_symbols::get_class("PRISM.Module.Networking.dll",
"PRISM.Module.Networking.Stub.Status", "CostumeStatus");
Expand Down Expand Up @@ -902,9 +982,42 @@ namespace
void* UnsafeLoadBytesFromKey_hook(void* _this, Il2CppString* tagName, Il2CppString* assetKey) {
auto ret = reinterpret_cast<decltype(UnsafeLoadBytesFromKey_hook)*>(UnsafeLoadBytesFromKey_orig)(_this, tagName, assetKey);
// wprintf(L"UnsafeLoadBytesFromKey: tag: %ls, assetKey: %ls\n", tagName->start_char, assetKey->start_char);
// dumpByteArray(tagName->start_char, assetKey->start_char, ret);
return ret;
}

void* TextLog_AddLog_orig;
void TextLog_AddLog_hook(void* _this, int speakerFlag, Il2CppString* textID, Il2CppString* text, bool isChoice) {
reinterpret_cast<decltype(TextLog_AddLog_hook)*>(TextLog_AddLog_orig)(_this, speakerFlag, textID, text, isChoice);

static auto TextLog_klass = il2cpp_symbols::get_class_from_instance(_this);
static auto dicConvertID_field = il2cpp_class_get_field_from_name(TextLog_klass, "dicConvertID");
static auto speakerTable_field = il2cpp_class_get_field_from_name(TextLog_klass, "speakerTable");
static auto listTextLogData_field = il2cpp_class_get_field_from_name(TextLog_klass, "listTextLogData");
static auto unitIdol_field = il2cpp_class_get_field_from_name(TextLog_klass, "unitIdol");
static auto convertList_field = il2cpp_class_get_field_from_name(TextLog_klass, "convertList");

static auto toJsonStr = reinterpret_cast<Il2CppString * (*)(void*)>(
il2cpp_symbols::get_method_pointer("Newtonsoft.Json.dll", "Newtonsoft.Json",
"JsonConvert", "SerializeObject", 1)
);

auto dicConvertID = il2cpp_field_get_value_object(dicConvertID_field, _this);
auto speakerTable = il2cpp_field_get_value_object(speakerTable_field, _this);
auto listTextLogData = il2cpp_field_get_value_object(listTextLogData_field, _this);
auto unitIdol = il2cpp_field_get_value_object(unitIdol_field, _this);
auto convertList = il2cpp_field_get_value_object(convertList_field, _this);

wprintf(L"TextLog_AddLog: speakerFlag: %d, textID: %ls, text: %ls\ndicConvertID:\n%ls\n\nspeakerTable:\n%ls\n\nlistTextLogData:\n%ls\n\nunitIdol:\n%ls\n\nconvertList:\n%ls\n\n",
speakerFlag, textID->start_char, text->start_char,
toJsonStr(dicConvertID)->start_char,
toJsonStr(speakerTable)->start_char,
toJsonStr(listTextLogData)->start_char,
toJsonStr(unitIdol)->start_char,
toJsonStr(convertList)->start_char
);
}

void* baseCameraTransform = nullptr;
void* baseCamera = nullptr;

Expand Down Expand Up @@ -1304,6 +1417,11 @@ namespace
"ResourceLoader", "UnsafeLoadBytesFromKey", 2
);

auto TextLog_AddLog_addr = il2cpp_symbols::get_method_pointer(
"PRISM.Legacy.dll", "PRISM.Scenario",
"TextLog", "AddLog", 4
);

auto LocalizationManager_GetTextOrNull_addr = il2cpp_symbols::get_method_pointer(
"ENTERPRISE.Localization.dll", "ENTERPRISE.Localization",
"LocalizationManager", "GetTextOrNull", 2
Expand Down Expand Up @@ -1382,6 +1500,16 @@ namespace
"LiveCostumeChangeModel", ".ctor", 3
);

auto AssembleCharacter_ApplyParam_addr = il2cpp_symbols::get_method_pointer(
"PRISM.Legacy.dll", "PRISM",
"AssembleCharacter", "ApplyParam", 6
);

auto MainThreadDispatcher_LateUpdate_addr = il2cpp_symbols::get_method_pointer(
"UniRx.dll", "UniRx",
"MainThreadDispatcher", "LateUpdate", 0
);

auto LiveMVUnit_GetMemberChangeRequestData_addr = il2cpp_symbols::get_method_pointer(
"PRISM.Legacy.dll", "PRISM.Live",
"LiveMVUnit", "GetMemberChangeRequestData", 3
Expand Down Expand Up @@ -1466,6 +1594,7 @@ namespace
ADD_HOOK(ScenarioManager_Init, "ScenarioManager_Init at %p");
ADD_HOOK(DataFile_GetBytes, "DataFile_GetBytes at %p");
ADD_HOOK(UnsafeLoadBytesFromKey, "UnsafeLoadBytesFromKey at %p");
ADD_HOOK(TextLog_AddLog, "TextLog_AddLog at %p");
ADD_HOOK(SetResolution, "SetResolution at %p");
ADD_HOOK(InvokeMoveNext, "InvokeMoveNext at %p");
ADD_HOOK(Live_SetEnableDepthOfField, "Live_SetEnableDepthOfField at %p");
Expand All @@ -1476,6 +1605,8 @@ namespace
ADD_HOOK(LiveCostumeChangeModel_GetHairstyle, "LiveCostumeChangeModel_GetHairstyle at %p");
ADD_HOOK(LiveCostumeChangeModel_GetAccessory, "LiveCostumeChangeModel_GetAccessory at %p");
ADD_HOOK(LiveCostumeChangeModel_ctor, "LiveCostumeChangeModel_ctor at %p");
ADD_HOOK(AssembleCharacter_ApplyParam, "AssembleCharacter_ApplyParam at %p");
ADD_HOOK(MainThreadDispatcher_LateUpdate, "MainThreadDispatcher_LateUpdate at %p");
ADD_HOOK(dic_int_ICostumeStatus_add, "dic_int_ICostumeStatus_add at %p");
ADD_HOOK(LiveMVUnitConfirmationModel_ctor, "LiveMVUnitConfirmationModel_ctor at %p");
// ADD_HOOK(PopupSystem_ShowPopup, "PopupSystem_ShowPopup at %p");
Expand Down
1 change: 1 addition & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ bool g_allow_use_tryon_costume = false;
bool g_allow_same_idol = false;
bool g_unlock_all_dress = false;
bool g_unlock_all_headwear = false;
bool g_enable_chara_param_edit = false;

std::filesystem::path g_localify_base("scsp_localify");
constexpr const char ConfigJson[] = "scsp-config.json";
Expand Down
93 changes: 77 additions & 16 deletions src/scgui/scGUILoop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
#include "scgui/scGUIData.hpp"

extern void* SetResolution_orig;
extern std::vector<std::pair<std::pair<int, int>, int>> replaceDressResIds;
// extern std::vector<std::pair<std::pair<int, int>, int>> replaceDressResIds;
extern std::map<std::string, CharaParam_t> charaParam;
extern CharaParam_t baseParam;


#define INPUT_AND_SLIDER_FLOAT(label, data, min, max) \
Expand All @@ -13,6 +15,13 @@ extern std::vector<std::pair<std::pair<int, int>, int>> replaceDressResIds;
ImGui::SetNextItemWidth(ImGui::CalcItemWidth() - inputFloatWidth - 1.0f);\
ImGui::SliderFloat(label, data, min, max)

#define FOR_INPUT_AND_SLIDER_FLOAT(label, data, min, max, hideIdName) \
ImGui::SetNextItemWidth(inputFloatWidth);\
ImGui::InputFloat(("##"##label + hideIdName).c_str(), data);\
ImGui::SameLine();\
ImGui::SetNextItemWidth(ImGui::CalcItemWidth() - inputFloatWidth - 1.0f);\
ImGui::SliderFloat((label##"##" + hideIdName).c_str(), data, min, max)

#define HELP_TOOLTIP(label, text) \
ImGui::TextDisabled(label); \
if (ImGui::IsItemHovered()) { \
Expand All @@ -24,22 +33,70 @@ extern std::vector<std::pair<std::pair<int, int>, int>> replaceDressResIds;
namespace SCGUILoop {
static float inputFloatWidth = 50.0f;

// 没用的
void dressReplaceSetLoop() {
if (ImGui::Begin("SC Dress Replace")) {
for (size_t i = 0; i < replaceDressResIds.size(); ++i) {
auto& dressData = replaceDressResIds[i];

ImGui::InputInt2(("CharaId, DressId##CharacterDressID" + std::to_string(i)).c_str(), &dressData.first.first);
ImGui::InputInt(("RepalceResId##ReplaceResID" + std::to_string(i)).c_str(), &dressData.second);

if (ImGui::Button(("Remove##" + std::to_string(i)).c_str())) {
replaceDressResIds.erase(replaceDressResIds.begin() + i);
--i;
void charaParamEditLoop() {
if (ImGui::Begin("Character Parameter Edit")) {
bool baseApply = false;
bool resetAll = false;
if (ImGui::CollapsingHeader("Base Offset", ImGuiTreeNodeFlags_DefaultOpen)) {
INPUT_AND_SLIDER_FLOAT("Height##base", &baseParam.height, -1.5, 1.5);
INPUT_AND_SLIDER_FLOAT("Bust##base", &baseParam.bust, -1.5, 1.5);
INPUT_AND_SLIDER_FLOAT("Head##base", &baseParam.head, -1.5, 1.5);
INPUT_AND_SLIDER_FLOAT("Arm##base", &baseParam.arm, -1.5, 1.5);
INPUT_AND_SLIDER_FLOAT("Hand##base", &baseParam.hand, -1.5, 1.5);
if (ImGui::Button("Apply##base")) {
baseApply = true;
}
ImGui::SameLine();
if (ImGui::Button("Reset Base##base")) {
baseParam.Reset();
baseApply = true;
}
ImGui::SameLine();
if (ImGui::Button("Reset All##base")) {
baseParam.Reset();
resetAll = true;
baseApply = true;
}
ImGui::SameLine();
if (ImGui::Checkbox("Real-time Update##base", &baseParam.gui_real_time_apply)) {
if (baseParam.gui_real_time_apply) {
baseApply = true;
}
}
}
if (ImGui::Button("Add")) {
replaceDressResIds.push_back({ {-1, -1}, -1 });

for (auto& i : charaParam) {
if (i.second.checkObjAlive()) {
if (ImGui::CollapsingHeader(i.first.c_str())) {
FOR_INPUT_AND_SLIDER_FLOAT("Height", &i.second.height, 0.0, 2.0, i.first);
FOR_INPUT_AND_SLIDER_FLOAT("Bust", &i.second.bust, 0.0, 2.0, i.first);
FOR_INPUT_AND_SLIDER_FLOAT("Head", &i.second.head, 0.0, 2.0, i.first);
FOR_INPUT_AND_SLIDER_FLOAT("Arm", &i.second.arm, 0.0, 2.0, i.first);
FOR_INPUT_AND_SLIDER_FLOAT("Hand", &i.second.hand, 0.0, 2.0, i.first);
if (ImGui::Button(("Apply##" + i.first).c_str()) || baseApply) {
i.second.ApplyOnMainThread();
}
ImGui::SameLine();
if (ImGui::Button(("Reset##" + i.first).c_str()) || resetAll) {
i.second.Reset();
i.second.ApplyOnMainThread();
}
ImGui::SameLine();
if (ImGui::Checkbox(("Real-time Update##" + i.first).c_str(), &i.second.gui_real_time_apply)) {
if (i.second.gui_real_time_apply) {
i.second.ApplyOnMainThread();
}
}
}
else {
if (baseApply) {
if (resetAll) {
i.second.Reset();
}
i.second.ApplyOnMainThread();
}
}
}
}
}
ImGui::End();
Expand Down Expand Up @@ -70,6 +127,10 @@ namespace SCGUILoop {
ImGui::SameLine();
HELP_TOOLTIP("(?)", "偶像的事,怎么能叫抢呢(\n切换到试穿模式换装后,切回普通模式,仍旧锁定试穿模式的衣服\n(此模式的编组数据会上传,请小心你的号)")

ImGui::Checkbox("Enable Character Parameter Editor", &g_enable_chara_param_edit);
ImGui::SameLine();
HELP_TOOLTIP("(?)", "启用角色身体参数编辑器")

if (ImGui::CollapsingHeader("Resolution Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::Button("720P")) {
SCGUIData::screenW = 1280;
Expand Down Expand Up @@ -125,6 +186,6 @@ namespace SCGUILoop {

}
ImGui::End();
// dressReplaceSetLoop();
if (g_enable_chara_param_edit) charaParamEditLoop();
}
}
2 changes: 1 addition & 1 deletion src/scgui/scGUIMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ bool guiDone = true;
bool attachToGame = false;

HWND hwnd;
RECT cacheRect{ 100, 100, 730, 880 };
RECT cacheRect{ 100, 100, 730, 910 };

void SetGuiDone(bool isDone) {
guiDone = isDone;
Expand Down
Loading

0 comments on commit 1443629

Please sign in to comment.