diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f913b36..1f3c867 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(app) add_subdirectory(async) add_subdirectory(client) +add_subdirectory(clientobject) add_subdirectory(console) add_subdirectory(cursor) add_subdirectory(db) diff --git a/src/client/Client.cpp b/src/client/Client.cpp index a8d90e8..f326581 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -6,6 +6,7 @@ #include "console/Client.hpp" #include "console/Device.hpp" #include "console/Screen.hpp" +#include "console/Command.hpp" #include "db/Db.hpp" #include "glue/CGlueMgr.hpp" #include "gx/Screen.hpp" @@ -21,9 +22,38 @@ #include #include #include +#include + +CVar* Client::g_accountNameVar; CVar* Client::g_accountListVar; +CVar* Client::g_accountUsesTokenVar; +CVar* Client::g_movieVar; +CVar* Client::g_expansionMovieVar; +CVar* Client::g_movieSubtitleVar; + + HEVENTCONTEXT Client::g_clientEventContext; +char Client::g_currentLocaleName[5] = {}; + + +static uint8_t s_expansionLevel = 0; +static bool g_hasIsoLocale[12] = {}; +static char* s_localeArray[12] = { + "deDE", "enGB", "enUS", "esES", "frFR", "koKR", + "zhCN", "zhTW", "enCN", "enTW", "esMX", "ruRU" +}; + + +int32_t CCommand_ReloadUI(const char*, const char*) { + // TODO: + return 1; +} + +int32_t CCommand_Perf(const char*, const char*) { + return 1; +} + void AsyncFileInitialize() { // TODO @@ -36,18 +66,20 @@ void BaseInitializeGlobal() { void ClientMiscInitialize() { // TODO +} - Client::g_accountListVar = CVar::Register( - "accountList", - "List of wow accounts for saved Blizzard account", - 0, - "", - nullptr, - 4, - false, - nullptr, - false - ); +void ClientRegisterConsoleCommands() { + ConsoleCommandRegister("reloadUI", &CCommand_ReloadUI, CATEGORY::GRAPHICS, nullptr); + ConsoleCommandRegister("perf", &CCommand_Perf, CATEGORY::DEBUG, nullptr); + + const auto game = CATEGORY::GAME; + + Client::g_accountNameVar = CVar::Register("accountName", "Saved account name", 64, "", nullptr, game); + Client::g_accountListVar = CVar::Register("accountList", "List of wow accounts for saved Blizzard account", 0, "", nullptr, game); + Client::g_accountUsesTokenVar = CVar::Register("g_accountUsesToken", "Saved whether uses authenticator", 0, "0", nullptr, game); + Client::g_movieVar = CVar::Register("movie", "Show movie on startup", 0, "1", nullptr, game); + Client::g_expansionMovieVar = CVar::Register("expansionMovie", "Show expansion movie on startup", 0, "1", nullptr, game); + Client::g_movieSubtitleVar = CVar::Register("movieSubtitle", "Show movie subtitles", 0, "0", nullptr, game); // TODO } @@ -139,6 +171,32 @@ int32_t InitializeEngineCallback(const void* a1, void* a2) { return 1; } +uint8_t GetExpansionLevel() { + return s_expansionLevel; +} + +const char* UpdateInstallLocation() { + // TODO + return nullptr; +} + +bool UpdateInstallLocationForName(int32_t a1, size_t size, const char* filename, char* buffer, const char* locale) { + if (a1 == 2) { + auto location = UpdateInstallLocation(); + if (!location) { + return false; + } + SStrPrintf(buffer, size, "%s%s%s", location, "Data\\", filename); + } else { + SStrPrintf(buffer, size, "%s%s", "Data\\", filename); + } + for (auto i = SStrStr(buffer, "****"); i; i = SStrStr(buffer, "****")) { + size_t offset = static_cast(i - buffer); + memcpy(&buffer[offset], locale, 4); + } + return true; +} + void SetPaths() { // SFile::DisableSFileCheckDisk(); // SFile::EnableDirectAccess(0); @@ -158,12 +216,109 @@ void SetPaths() { OsSetCurrentDirectory(datadir); } +bool IsCommonMpqExists() { + char path1[1024]; + SStrPrintf(path1, sizeof(path1), "%s%s", "Data\\", "common.MPQ"); + for (auto i = SStrStr(path1, "****"); i; i = SStrStr(path1, "****")) { + size_t offset = static_cast(i - path1); + memcpy(&path1[offset], "----", 4); + } + + char path2[1024]; + SStrPrintf(path2, sizeof(path2), "%s%s", "..\\Data\\", "common.MPQ"); + for (auto i = SStrStr(path2, "****"); i; i = SStrStr(path2, "****")) { + size_t offset = static_cast(i - path2); + memcpy(&path2[offset], "----", 4); + } + + auto location = UpdateInstallLocation(); + if (location) { + char path3[1024]; + SStrPrintf(path3, sizeof(path3), "%s%s%s", location, "Data\\", "common.MPQ"); + for (auto i = SStrStr(path3, "****"); i; i = SStrStr(path3, "****")) { + size_t offset = static_cast(i - path3); + memcpy(&path3[offset], "----", 4); + } + + if (!Blizzard::File::Exists(path1) && !Blizzard::File::Exists(path2)) { + return Blizzard::File::Exists(path3); + } + } else if (!Blizzard::File::Exists(path1)) { + return Blizzard::File::Exists(path2); + } + + return true; +} + +size_t GetLocaleIndex(const char* locale) { + for (size_t i = 0; i < 12; ++i) { + if (SStrCmpI(locale, s_localeArray[i], 4) == 0) { + return i; + } + } + return 2; // s_localeArray[2] == "enUS" +} + +void CheckAvailableLocales(char* locale) { + if (!IsCommonMpqExists()) { + return; + } + + for (size_t localeIndex = 0; localeIndex < 12; ++localeIndex) { + g_hasIsoLocale[localeIndex] = false; + + const char* filename = "****\\locale-****.MPQ"; + + char path[1024]; + SStrPrintf(path, sizeof(path), "%s%s", "Data\\", filename); + for (auto i = SStrStr(path, "****"); i; i = SStrStr(path, "****")) { + size_t offset = static_cast(i - path); + memcpy(&path[offset], s_localeArray[localeIndex], 4); + } + + if (Blizzard::File::Exists(path)) { + g_hasIsoLocale[localeIndex] = true; + continue; + } + + SStrPrintf(path, sizeof(path), "%s%s", "..\\Data\\", filename); + for (auto i = SStrStr(path, "****"); i; i = SStrStr(path, "****")) { + size_t offset = static_cast(i - path); + memcpy(&path[offset], s_localeArray[localeIndex], 4); + } + + if (Blizzard::File::Exists(path)) { + g_hasIsoLocale[localeIndex] = true; + continue; + } + + if (UpdateInstallLocationForName(2, sizeof(path), filename, path, s_localeArray[localeIndex]) && + Blizzard::File::Exists(path)) { + g_hasIsoLocale[localeIndex] = true; + } + } + + size_t localeIndex = GetLocaleIndex(locale); + for (size_t i = 0; i < 12; ++i) { + if (g_hasIsoLocale[localeIndex]) { + break; + } + localeIndex = (localeIndex + 1) % 12; + } + SStrCopy(locale, s_localeArray[localeIndex], STORM_MAX_STR); +} + +bool LocaleChangedCallback(CVar*, const char*, const char* value, void*) { + SStrCopy(Client::g_currentLocaleName, value, sizeof(Client::g_currentLocaleName)); + return true; +} + int32_t InitializeGlobal() { - // TODO - ProcessCommandLine(); + SetPaths(); - // sub_403600("WoW.mfil"); + // TODO: + // WowConfigureFileSystem::ReadBuildKeyFromFile("WoW.mfil"); // if (dword_B2FA10 != 2) { // sub_403560(); @@ -175,58 +330,83 @@ int32_t InitializeGlobal() { // LOBYTE(v24) = OsDirectoryExists((int)"WTF/Account") == 0; // } - // ClientServices::LoadCDKey(); - - SetPaths(); - - OpenArchives(); + ClientServices::LoadCDKey(); ConsoleInitializeClientCommand(); - ConsoleInitializeClientCVar("Config.wtf"); - - // TODO - // replace enUS with detected locale - ClientServices::InitLoginServerCVars(1, "enUS"); - - // sub_7663F0(); + // TODO: CVar::ArchiveCodeRegisteredOnly(); // v18 = 0; // v19 = 0; // ptr = 0; // v21 = 0; - // sub_406740(&v18, &CVar::Load); + // ::ForEveryRunOnceWTF::Execute(&v18, &CVar::Load); // if (ptr) { // SMemFree(ptr, a_pad, -2, 0); // } - // CVar::Register("dbCompress", "Database compression", 0, "-1", 0, 5, 0, 0, 0); + CVar::Register( + "dbCompress", + "Database compression", + 0, + "-1", + nullptr, + CATEGORY::DEFAULT, + false, + nullptr, + false + ); - // v2 = CVar::Register("locale", "Set the game locale", 0, "****", &LocaleChangedCallback, 5, 0, 0, 0); + CVar* locale = CVar::Register( + "locale", + "Set the game locale", + 0, + "****", + &LocaleChangedCallback, + CATEGORY::DEFAULT, + false, + nullptr, + false + ); - // if (!SStrCmp(v2->m_stringValue.m_str, "****", 0x7FFFFFFFu)) { - // CVar::Set(v2, "enUS", 1, 0, 0, 1); - // } + if (!SStrCmp(locale->GetString(), "****", STORM_MAX_STR)) { + locale->Set("enUS", true, false, false, true); + } - // CVar::Register("useEnglishAudio", "override the locale and use English audio", 0, "0", 0, 5, 0, 0, 0); + CVar::Register( + "useEnglishAudio", + "override the locale and use English audio", + 0, + "0", + nullptr, + CATEGORY::DEFAULT, + false, + nullptr, + false + ); + // TODO: SFile::IsTrial() check // if (sub_422140()) { // sub_4036B0(v24, 0, a2, (int)v2, (char)v24); // } - // SStrCopy(&a1a, v2->m_stringValue.m_str, 5); + char existingLocale[5] = {}; + SStrCopy(existingLocale, locale->GetString(), sizeof(existingLocale)); + CheckAvailableLocales(existingLocale); + locale->Set(existingLocale, true, false, false, true); - // sub_402D50(&a1a); + char path[STORM_MAX_PATH]; + SStrPrintf(path, sizeof(path), "%s%s", "Data\\", locale->GetString()); + SFile::SetDataPathAlternate(path); + SFile::RebuildHash(); - // CVar::Set(v2, &a1a, 1, 0, 0, 1); - // SStrPrintf(dest, 260, "%s%s", *(_DWORD *)off_AB6158, v2->m_stringValue.m_str); + OpenArchives(); - // sub_421B50(dest); - - // sub_423D70(); + // TODO: This method should be placed inside OpenArchives + ClientServices::InitLoginServerCVars(1, locale->GetString()); // sub_405DD0(); @@ -343,19 +523,36 @@ int32_t InitializeGlobal() { void CommonMain() { StormInitialize(); - // TODO - // - error log setup - // - misc other setup + // TODO: + // SErrCatchUnhandledExceptions(); + // OsSystemInitialize("Blizzard Entertainment World of Warcraft", 0); + // int option = 1; + // StormSetOption(10, &option, sizeof(option)); + // StormSetOption(11, &option, sizeof(option)); + // OsSystemEnableCpuLog(); + + // SetPaths() moved into InitializeGlobal() + + // int sendErrorLogs = 1; + // if (!SRegLoadValue("World of Warcraft\\Client", "SendErrorLogs", 0, &sendErrorLogs)) { + // sendErrorLogs = 1; + // SRegSaveValue("World of Warcraft\\Client", "SendErrorLogs", 0, sendErrorLogs); + // } + + // SErrSetLogTitleString("World of WarCraft (build 12340)"); + // SErrSetLogTitleCallback(WowLogHeader); + // if (sendErrorLogs) { + // SErrRegisterHandler(SendErrorLog); + // } if (InitializeGlobal()) { EventDoMessageLoop(); - - // TODO - // sub_406B70(); + // TODO: DestroyGlobal(); } - // TODO - // - misc cleanup + // TODO: + // StormDestroy(); + // Misc Cleanup } void BlizzardAssertCallback(const char* a1, const char* a2, const char* a3, uint32_t a4) { @@ -383,7 +580,7 @@ void WowClientInit() { ClientMiscInitialize(); - // sub_401B60(); + ClientRegisterConsoleCommands(); ClientDBInitialize(); @@ -441,27 +638,22 @@ void WowClientInit() { // sub_421630(); // } - // TODO - // if (byte_B2F9E1 != 1) { - // if ((g_playIntroMovie + 48) == 1) { - // CVar::Set(g_playIntroMovie, "0", 1, 0, 0, 1); - // CGlueMgr::SetScreen("movie"); - // } else { - // CGlueMgr::SetScreen("login"); - // } - // } else { - // if ((dword_B2F980 + 48) == 1) { - // CVar::Set(dword_B2F980, "0", 1, 0, 0, 1); - // CVar::Set(g_playIntroMovie, "0", 1, 0, 0, 1); - // CGlueMgr::SetScreen("movie"); - // } else { - // CGlueMgr::SetScreen("login"); - // } - // } - - // TODO - // - temporary until above logic is implemented - CGlueMgr::SetScreen("login"); + if (s_expansionLevel != 1) { + if (Client::g_movieVar->GetInt()) { + Client::g_movieVar->Set("0", true, false, false, true); + CGlueMgr::SetScreen("movie"); + } else { + CGlueMgr::SetScreen("login"); + } + } else { + if (Client::g_expansionMovieVar->GetInt()) { + Client::g_expansionMovieVar->Set("0", true, false, false, true); + Client::g_movieVar->Set("0", true, false, false, true); + CGlueMgr::SetScreen("movie"); + } else { + CGlueMgr::SetScreen("login"); + } + } // TODO // CGlueMgr::m_pendingTimerAlert = dword_B2F9D8; diff --git a/src/client/Client.hpp b/src/client/Client.hpp index 7d3f60b..1c580ff 100644 --- a/src/client/Client.hpp +++ b/src/client/Client.hpp @@ -7,12 +7,20 @@ class CVar; namespace Client { + extern CVar* g_accountNameVar; extern CVar* g_accountListVar; + extern CVar* g_accountUsesTokenVar; + extern CVar* g_movieVar; + extern CVar* g_expansionMovieVar; + extern CVar* g_movieSubtitleVar; extern HEVENTCONTEXT g_clientEventContext; + extern char g_currentLocaleName[5]; } void ClientPostClose(int32_t a1); +const char* UpdateInstallLocation(); + void CommonMain(); void StormInitialize(); diff --git a/src/client/ClientRealmResponseAdapter.cpp b/src/client/ClientRealmResponseAdapter.cpp index 7a835b7..f4f1d63 100644 --- a/src/client/ClientRealmResponseAdapter.cpp +++ b/src/client/ClientRealmResponseAdapter.cpp @@ -16,3 +16,13 @@ void ClientRealmResponseAdapter::HandleAuthResponse(RealmConnection* realmConnec AccountDataInitialize(true); } + +void ClientRealmResponseAdapter::CharacterListReceived(RealmConnection* realmConnection, void* a2, int32_t success) { + auto clientConnection = static_cast(realmConnection); + + if (success) { + clientConnection->Complete(1, 44); + } else { + clientConnection->Complete(1, 45); + } +} diff --git a/src/client/ClientRealmResponseAdapter.hpp b/src/client/ClientRealmResponseAdapter.hpp index 1c0c2ae..4ef4569 100644 --- a/src/client/ClientRealmResponseAdapter.hpp +++ b/src/client/ClientRealmResponseAdapter.hpp @@ -7,6 +7,7 @@ class ClientRealmResponseAdapter : public RealmResponse { public: // Virtual member functions virtual void HandleAuthResponse(RealmConnection* realmConnection, uint8_t authResult); + virtual void CharacterListReceived(RealmConnection* realmConnection, void* a2, int32_t success); virtual void GameServerResult(RealmConnection* realmConnection, const char* a2, const char* a3, const char* a4) {}; }; diff --git a/src/client/ClientServices.cpp b/src/client/ClientServices.cpp index fc55a16..7ce248e 100644 --- a/src/client/ClientServices.cpp +++ b/src/client/ClientServices.cpp @@ -66,6 +66,14 @@ ClientServices* ClientServices::GetInstance() { return ClientServices::s_instance; } +void ClientServices::GetRealmList() { + // TODO +} + +void ClientServices::GetCharacterList() { + ClientServices::s_currentConnection->GetCharacterList(); +} + REALM_INFO* ClientServices::GetRealmInfoByIndex(int32_t index) { if (index >= ClientServices::GetInstance()->m_realmList.Count()) { return nullptr; @@ -264,6 +272,11 @@ const char* ClientServices::GetDefaultPatchListString() { return "public-test.patch.battle.net:1119/patch"; } +bool ClientServices::LoadCDKey() { + // TODO + return true; +} + void ClientServices::InitLoginServerCVars(int32_t overwrite, const char* locale) { if ((ClientServices::s_realmListBNVar == nullptr || ClientServices::s_realmListVar == nullptr) || overwrite != 0 ) { ClientServices::s_decorateAccountName = CVar::Register( diff --git a/src/client/ClientServices.hpp b/src/client/ClientServices.hpp index e203cc7..f664f92 100644 --- a/src/client/ClientServices.hpp +++ b/src/client/ClientServices.hpp @@ -33,6 +33,8 @@ class ClientServices : public LoginResponse { static void ConnectToSelectedServer(); static ClientConnection* Connection(); static ClientServices* GetInstance(); + static void GetRealmList(); + static void GetCharacterList(); static REALM_INFO* GetRealmInfoByIndex(int32_t index); static const char* GetSelectedRealmName(); static const REALM_INFO* GetSelectedRealm(); @@ -45,6 +47,7 @@ class ClientServices : public LoginResponse { static void InitLoginServerCVars(int32_t overwrite, const char* locale); static const char* GetDefaultRealmlistString(); static const char* GetDefaultPatchListString(); + static bool LoadCDKey(); // Virtual member functions virtual int32_t GetLoginServerType(); diff --git a/src/client/Patch.cpp b/src/client/Patch.cpp new file mode 100644 index 0000000..0e63151 --- /dev/null +++ b/src/client/Patch.cpp @@ -0,0 +1,65 @@ +#include "client/Patch.hpp" + +#include + +#include "client/Client.hpp" + + +PatchFiles::PatchFiles() { + this->path = nullptr; +} + +void PatchFiles::SearchArchives(const char* locale, int32_t a3) { + char fullPath[256] = {}; + char pattern[256] = {}; + char path[256] = {}; + + const size_t listSize = 12; + + ListEntry list[listSize] = { + { 0, 0, false, "Data\\", "patch-?.MPQ" }, + { 0, 0, false, "Data\\%s\\", "patch-%s-?.MPQ" }, + { 1, 0, false, "Data\\", "patch.MPQ" }, + { 1, 0, false, "Data\\%s\\", "patch-%s.MPQ" }, + { 1, 1, false, "Data\\", "patch-4.MPQ" }, + { 1, 1, false, "Data\\%s\\", "patch-%s-4.MPQ" }, + { 1, 1, false, "Data\\", "patch-3.MPQ" }, + { 1, 1, false, "Data\\%s\\", "patch-%s-3.MPQ" }, + { 1, 1, false, "..\\Data\\", "patch-2.MPQ" }, + { 1, 1, false, "..\\Data\\%s\\", "patch-%s-2.MPQ" }, + { 1, 1, false, "..\\Data\\", "patch.MPQ" }, + { 1, 1, false, "..\\Data\\%s\\", "patch-%s.MPQ" }, + }; + + const char* installRoot = UpdateInstallLocation(); + + // Warning: assigning pointer to a temporary variable + // Need this for OsFileList's callback + this->path = path; + + for (size_t i = 0; i <= 2; ++i) { + for (size_t j = 0; j < listSize; ++j) { + auto entry = &list[j]; + if (entry->num1 != i || entry->num2 != (a3 != 0)) { + continue; + } + + SStrPrintf(path, sizeof(path), entry->dataDir, locale); + SStrPrintf(pattern, sizeof(pattern), entry->pattern, locale); + if (entry->absolute && installRoot && installRoot[0]) { + SStrPrintf(fullPath, sizeof(fullPath), "%s%s", installRoot, path); + SStrCopy(path, fullPath, sizeof(path)); + + } + // TODO: OsFileList(this->path, pattern, PatchFiles::EnumPatchArchives, this, 0); + } + + if (i == 0) { + std::qsort(this->files.Ptr(), this->files.Count(), sizeof(char*), &PatchFiles::qsortpatchfiles); + } + } +} + +int PatchFiles::qsortpatchfiles(const void* a1, const void* a2) { + return -SStrCmpI(*((const char**) a1), *((const char**) a2), STORM_MAX_STR); +} diff --git a/src/client/Patch.hpp b/src/client/Patch.hpp new file mode 100644 index 0000000..443c879 --- /dev/null +++ b/src/client/Patch.hpp @@ -0,0 +1,33 @@ +#ifndef CLIENT_PATCH_HPP +#define CLIENT_PATCH_HPP + + +#include + + +class PatchFiles { + public: + PatchFiles(); + + void SearchArchives(const char* locale, int32_t a3); + + public: + char* path; + TSGrowableArray files; + + private: + static int qsortpatchfiles(const void* a1, const void* a2); + + private: + struct ListEntry { + uint32_t num1; + uint32_t num2; + bool absolute; + const char* dataDir; + const char* pattern; + }; +}; + + + +#endif diff --git a/src/clientobject/CMakeLists.txt b/src/clientobject/CMakeLists.txt new file mode 100644 index 0000000..d84e320 --- /dev/null +++ b/src/clientobject/CMakeLists.txt @@ -0,0 +1,16 @@ +file(GLOB PRIVATE_SOURCES "*.cpp") + +add_library(clientobject STATIC + ${PRIVATE_SOURCES} +) + +target_include_directories(clientobject + PRIVATE + ${CMAKE_SOURCE_DIR}/src +) + +target_link_libraries(clientobject + PRIVATE + storm + db +) diff --git a/src/clientobject/Unit_C.cpp b/src/clientobject/Unit_C.cpp new file mode 100644 index 0000000..fe9436a --- /dev/null +++ b/src/clientobject/Unit_C.cpp @@ -0,0 +1,43 @@ +#include "clientobject/Unit_C.hpp" + +const char* CGUnit_C::GetDisplayRaceNameFromRecord(ChrRacesRec* record, uint8_t sexIn, uint8_t* sexOut) { + const char* result; + + if (sexOut) { + *sexOut = sexIn; + } + if (!record) { + return nullptr; + } + if (!sexIn) { + if (record->m_nameMale[0]) { + return record->m_nameMale; + } + + if (record->m_nameFemale[0]) { + if (sexOut) { + *sexOut = 1; + } + return record->m_nameFemale; + } + + return record->m_name; + } + + if (sexIn != 1) { + return record->m_name; + } + + if (record->m_nameFemale[0]) { + return record->m_nameFemale; + } + + if (!record->m_nameMale[0]) { + return record->m_name; + } + + if (sexOut) { + *sexOut = 0; + } + return record->m_nameMale; +} diff --git a/src/clientobject/Unit_C.hpp b/src/clientobject/Unit_C.hpp new file mode 100644 index 0000000..01299ba --- /dev/null +++ b/src/clientobject/Unit_C.hpp @@ -0,0 +1,11 @@ +#ifndef CLIENTOBJECT_UNIT_C_HPP +#define CLIENTOBJECT_UNIT_C_HPP + +#include "db/rec/ChrRacesRec.hpp" + +class CGUnit_C { + public: + static const char* GetDisplayRaceNameFromRecord(ChrRacesRec* record, uint8_t sexIn, uint8_t* sexOut = nullptr); +}; + +#endif // CLIENTOBJECT_UNIT_C_HPP diff --git a/src/console/CVar.cpp b/src/console/CVar.cpp index c7349e8..5a3b05e 100644 --- a/src/console/CVar.cpp +++ b/src/console/CVar.cpp @@ -17,7 +17,7 @@ CVar* CVar::Lookup(const char* name) { : nullptr; } -CVar* CVar::Register(const char* name, const char* help, uint32_t flags, const char* value, bool (*fcn)(CVar*, const char*, const char*, void*), uint32_t category, bool a7, void* arg, bool a9) { +CVar* CVar::Register(const char* name, const char* help, uint32_t flags, const char* value, bool (*fcn)(CVar*, const char*, const char*, void*), uint32_t category, bool setCommand, void* arg, bool a9) { CVar* var = CVar::s_registeredCVars.Ptr(name); if (var) { @@ -36,7 +36,7 @@ CVar* CVar::Register(const char* name, const char* help, uint32_t flags, const c var->Set(value, setValue, setReset, setDefault, false); - if (!a7) { + if (!setCommand) { var->m_flags |= 0x80000000; } @@ -59,7 +59,7 @@ CVar* CVar::Register(const char* name, const char* help, uint32_t flags, const c var->m_arg = arg; var->m_help.Copy(help); - if (a7) { + if (setCommand) { var->Set(value, true, true, false, false); } else { var->Set(value, true, false, true, false); @@ -67,7 +67,7 @@ CVar* CVar::Register(const char* name, const char* help, uint32_t flags, const c var->m_flags = flags | 0x1; - if (!a7) { + if (!setCommand) { var->m_flags |= 0x8000000; } diff --git a/src/console/CVar.hpp b/src/console/CVar.hpp index f1e5451..41df648 100644 --- a/src/console/CVar.hpp +++ b/src/console/CVar.hpp @@ -6,6 +6,8 @@ #include #include +#include "console/Types.hpp" + class CVar : public TSHashObject { public: // Static variables @@ -14,7 +16,17 @@ class CVar : public TSHashObject { // Static functions static CVar* Lookup(const char* name); - static CVar* Register(const char*, const char*, uint32_t, const char*, bool (*)(CVar*, const char*, const char*, void*), uint32_t, bool, void*, bool); + static CVar* Register( + const char* name, + const char* help, + uint32_t flags, + const char* value, + bool (*fcn)(CVar*, const char*, const char*, void*) = nullptr, + uint32_t category = CATEGORY::DEFAULT, + bool setCommand = false, + void* arg = nullptr, + bool a9 = false + ); static void Initialize(const char* filename); static int32_t Load(const char* filename); static int32_t Load(HOSFILE fileHandle); @@ -37,7 +49,7 @@ class CVar : public TSHashObject { int32_t GetInt(); const char* GetString(void); void InternalSet(const char*, bool, bool, bool, bool); - bool Set(const char*, bool, bool, bool, bool); + bool Set(const char* value, bool setValue, bool setReset, bool setDefault, bool a6); bool Reset(); bool Default(); int32_t Update(); diff --git a/src/console/Device.cpp b/src/console/Device.cpp index 5a1df3b..c8b9fcb 100644 --- a/src/console/Device.cpp +++ b/src/console/Device.cpp @@ -3,11 +3,32 @@ #include "client/Gui.hpp" #include "console/Console.hpp" #include "console/CVar.hpp" +#include "console/Command.hpp" #include "event/Input.hpp" +#include "gx/Gx.hpp" #include "gx/Device.hpp" #include #include + +CVar* s_cvHwDetect; +CVar* s_cvGxFixedFunction; +CVar* s_cvGxWindowResizeLock; +CVar* s_cvGxVideoOptionsVersion; +CVar* s_cvGxMaxFPSBk; +CVar* s_cvGxMaxFPS; +CVar* s_cvGxOverride; +CVar* s_cvGxStereoEnabled; +CVar* s_cvGxFixLag; +CVar* s_cvGxMultisampleQuality; +CVar* s_cvGxMultisample; +CVar* s_cvGxCursor; +CVar* s_cvGxAspect; +CVar* s_cvGxVSync; +CVar* s_cvGxTripleBuffer; +CVar* s_cvGxRefresh; +CVar* s_cvGxDepthBits; +CVar* s_cvGxColorBits; CVar* s_cvGxMaximize; CVar* s_cvGxResolution; CVar* s_cvGxWidescreen; @@ -44,6 +65,87 @@ EGxApi g_gxApiSupported[] = { size_t g_numGxApiSupported = sizeof(g_gxApiSupported) / sizeof(EGxApi); + +bool CVGxWindowResizeLockCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool GxVideoOptionsVersionCallback(CVar*, const char*, const char*, void*) { + return true; +} + +bool CVGxMaxFPSBkCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxMaxFPSCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxOverrideCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxStereoEnabledCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxFixLagCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxMultisampleQualityCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxMultisampleCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxCursorCallback(CVar*, const char*, const char* value, void*) { + s_requestedFormat.hwCursor = SStrToInt(value) != 0; + ConsoleWrite("set pending gxRestart", DEFAULT_COLOR); + return true; +} + +bool CVGxAspectCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxVSyncCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxTripleBufferCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxRefreshCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxDepthBitsCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + +bool CVGxColorBitsCallback(CVar*, const char*, const char*, void*) { + // TODO + return true; +} + bool CVGxMaximizeCallback(CVar*, const char*, const char*, void*) { // TODO return true; @@ -92,6 +194,11 @@ bool CVGxApiCallback(CVar* h, const char* oldValue, const char* newValue, void* return false; } +int32_t CCGxRestart(const char*, const char*) { + // TODO + return 1; +} + EGxApi GxApiDefault() { #if defined(WHOA_SYSTEM_WIN) return GxApi_D3d9; @@ -107,112 +214,76 @@ EGxApi GxApiDefault() { } void RegisterGxCVars() { - auto& format = s_defaults.format; + const auto& format = s_defaults.format; - // TODO CURRENT_LANGUAGE check? - auto v1 = true; + // TODO: bool isChinese = s_currentLocaleIndex == 4 (zhCN) + bool isChinese = false; - s_cvGxWidescreen = CVar::Register( - "widescreen", - "Allow widescreen support", - 0x0, - "1", - nullptr, - 1, - false, - nullptr, - false - ); + const uint32_t graphics = CATEGORY::GRAPHICS; - s_cvGxWindow = CVar::Register( - "gxWindow", - "toggle fullscreen/window", - 0x1 | 0x2, - v1 ? "1" : "0", - &CVGxWindowCallback, - 1, - 0, - 0, - 0 - ); + s_cvGxWidescreen = CVar::Register("widescreen", "Allow widescreen support", 0x0, "1", nullptr, graphics); + s_cvGxWindow = CVar::Register("gxWindow", "toggle fullscreen/window", 0x1 | 0x2, isChinese ? "1" : "0", &CVGxWindowCallback, graphics); + s_cvGxMaximize = CVar::Register("gxMaximize", "maximize game window", 0x1 | 0x2, isChinese ? "1" : "0", &CVGxMaximizeCallback, graphics); - s_cvGxMaximize = CVar::Register( - "gxMaximize", - "maximize game window", - 0x1 | 0x2, - v1 ? "1" : "0", - &CVGxMaximizeCallback, - 1, - 0, - 0, - 0 - ); + char value[260] = {}; + SStrPrintf(value, sizeof(value), "%s", CGxFormat::formatToColorBitsString[format.colorFormat]); + s_cvGxColorBits = CVar::Register("gxColorBits", "color bits", 0x1 | 0x2, value, &CVGxColorBitsCallback, graphics); - // TODO s_cvGxColorBits - // TODO s_cvGxDepthBits + SStrPrintf(value, sizeof(value), "%s", CGxFormat::formatToColorBitsString[format.depthFormat]); + s_cvGxDepthBits = CVar::Register("gxDepthBits", "depth bits", 0x1 | 0x2, value, &CVGxDepthBitsCallback, graphics); - char resolution[260]; - SStrPrintf(resolution, 260, "%dx%d", format.size.x, format.size.y); - s_cvGxResolution = CVar::Register( - "gxResolution", - "resolution", - 0x1 | 0x2, - resolution, - &CVGxResolutionCallback, - 1, - false, - nullptr, - false - ); + SStrPrintf(value, 260, "%dx%d", format.size.x, format.size.y); + s_cvGxResolution = CVar::Register("gxResolution", "resolution", 0x1 | 0x2, value, &CVGxResolutionCallback, graphics); - // TODO s_cvGxRefresh - // TODO s_cvGxTripleBuffer - // TODO s_cvGxApi + s_cvGxRefresh = CVar::Register("gxRefresh", "refresh rate", 0x1 | 0x2, "75", &CVGxRefreshCallback, graphics); + s_cvGxTripleBuffer = CVar::Register("gxTripleBuffer", "triple buffer", 0x1 | 0x2, "0", &CVGxTripleBufferCallback, graphics); + s_cvGxApi = CVar::Register("gxApi", "graphics api", 0x1 | 0x2, g_gxApiNames[GxApiDefault()], &CVGxApiCallback, graphics); - s_cvGxApi = CVar::Register( - "gxApi", - "graphics api", - 0x1 | 0x2, - g_gxApiNames[GxApiDefault()], - CVGxApiCallback, - 1, - false, - nullptr, - false - ); + s_cvGxVSync = CVar::Register("gxVSync", "vsync on or off", 0x1 | 0x2, "1", &CVGxVSyncCallback, graphics); + s_cvGxAspect = CVar::Register("gxAspect", "constrain window aspect", 0x1 | 0x2, "1", &CVGxAspectCallback, graphics); - // TODO s_cvGxVSync - // TODO s_cvGxAspect - // TODO s_cvGxCursor - // TODO s_cvGxMultisample - // TODO s_cvGxFixLag - // TODO s_cvGxStereoEnabled - // TODO s_cvGxOverride - // TODO s_cvGxAspect - // TODO s_cvGxMaxFPS - // TODO s_cvGxMaxFPSBk - // TODO s_cvWindowResizeLock - // TODO s_cvFixedFunction + s_cvGxCursor = CVar::Register("gxCursor", "toggle hardware cursor", 0x1 | 0x2, "1", &CVGxCursorCallback, graphics); + + // TODO: v10 = *(_DWORD*)(dword_CABB60 + 84); + int v10 = 0; + SStrPrintf(value, sizeof(value), "%d", v10); + s_cvGxMultisample = CVar::Register("gxMultisample", "multisample", 0x1 | 0x2, value, &CVGxMultisampleCallback, graphics); + s_cvGxMultisampleQuality = CVar::Register("gxMultisampleQuality", "multisample quality", 0x1 | 0x2, "0.0", &CVGxMultisampleQualityCallback, graphics); + + // TODO: v10 = *(_DWORD*)(dword_CABB60 + 80); + SStrPrintf(value, sizeof(value), "%d", v10); + s_cvGxFixLag = CVar::Register("gxFixLag", "prevent cursor lag", 0x1 | 0x2, value, &CVGxFixLagCallback, graphics); + s_cvGxStereoEnabled = CVar::Register("gxStereoEnabled", "Enable stereoscopic rendering", 0x1, "0", &CVGxStereoEnabledCallback, graphics); + s_cvGxOverride = CVar::Register("gxOverride", "gx overrides", 0x1, "", &CVGxOverrideCallback, graphics); + s_cvGxMaxFPS = CVar::Register("maxFPS", "Set FPS limit", 0x1, "200", &CVGxMaxFPSCallback, graphics); + s_cvGxMaxFPSBk = CVar::Register("maxFPSBk", "Set background FPS limit", 0x1, "30", &CVGxMaxFPSBkCallback, graphics); + s_cvGxVideoOptionsVersion = CVar::Register("videoOptionsVersion", "Video options version", 0x1 | 0x2, "0", &GxVideoOptionsVersionCallback, graphics); + s_cvGxWindowResizeLock = CVar::Register("windowResizeLock", "prevent resizing in windowed mode", 1, "0", &CVGxWindowResizeLockCallback, graphics); + s_cvGxFixedFunction = CVar::Register("fixedFunction", "Force fixed function rendering", 0x1 | 0x2, "0", 0, graphics); } void UpdateGxCVars() { - // TODO others - + s_cvGxColorBits->Update(); + s_cvGxDepthBits->Update(); s_cvGxWindow->Update(); s_cvGxResolution->Update(); - - // TODO others - + s_cvGxRefresh->Update(); + s_cvGxTripleBuffer->Update(); + s_cvGxApi->Update(); + s_cvGxVSync->Update(); + s_cvGxAspect->Update(); s_cvGxMaximize->Update(); - - // TODO others + s_cvGxCursor->Update(); + s_cvGxMultisample->Update(); + s_cvGxMultisampleQuality->Update(); + s_cvGxFixLag->Update(); } void SetGxCVars(const CGxFormat& format) { - char value[1024]; + char value[1024] = {}; - // TODO s_cvGxColorBits - // TODO s_cvGxDepthBits + s_cvGxColorBits->Set(CGxFormat::formatToColorBitsString[format.colorFormat], true, false, false, true); + s_cvGxDepthBits->Set(CGxFormat::formatToColorBitsString[format.depthFormat], true, false, false, true); SStrPrintf(value, sizeof(value), "%d", format.window); s_cvGxWindow->Set(value, true, false, false, true); @@ -220,43 +291,106 @@ void SetGxCVars(const CGxFormat& format) { SStrPrintf(value, sizeof(value), "%dx%d", format.size.x, format.size.y); s_cvGxResolution->Set(value, true, false, false, true); - // TODO s_cvGxRefresh - // TODO others + SStrPrintf(value, sizeof(value), "%d", format.refreshRate); + s_cvGxRefresh->Set(value, true, false, false, true); + + // TODO: (format + 28) > 1 + s_cvGxTripleBuffer->Set("0", true, false, false, true); + + SStrPrintf(value, sizeof(value), "%d", format.vsync); + s_cvGxVSync->Set(value, true, false, false, true); + + // TODO: format.aspectRatio + SStrPrintf(value, sizeof(value), "%d", 0); + s_cvGxAspect->Set(value, true, false, false, true); SStrPrintf(value, sizeof(value), "%d", format.maximize); s_cvGxMaximize->Set(value, true, false, false, true); - // TODO others + SStrPrintf(value, sizeof(value), "%d", format.hwCursor); + s_cvGxCursor->Set(value, true, false, false, true); + + SStrPrintf(value, sizeof(value), "%d", format.sampleCount); + s_cvGxMultisample->Set(value, true, false, false, true); + + // TODO: format.multisampleQuality + SStrPrintf(value, sizeof(value), "%f", 0.0f); + s_cvGxMultisampleQuality->Set(value, true, false, false, true); + + SStrPrintf(value, sizeof(value), "%d", format.fixLag); + s_cvGxFixLag->Set(value, true, false, false, true); UpdateGxCVars(); } void ConsoleDeviceInitialize(const char* title) { - // TODO + GxLogOpen(); + + s_cvHwDetect = CVar::Register("hwDetect", "do hardware detection", 0x1, "1", nullptr, CATEGORY::GRAPHICS); + + // TODO: sub_76BA30(&unk_CABB38, &byte_CABCBD); << ConsoleDetect + // TODO: byte_CABCBC = 1; + + if (CmdLineGetBool(WOWCMD_HW_DETECT) || s_cvHwDetect->GetInt() != 0) { + s_hwDetect = true; + s_cvHwDetect->Set("0", true, false, false, true); + } else { + s_hwDetect = false; + } - // TODO proper logic - s_hwDetect = true; ConsoleAccessSetEnabled(CmdLineGetBool(WOWCMD_CONSOLE)); - // TODO + // TODO: sub_76B520(&unk_CABAF0, &unk_CABB38); + + // CHANGE: Remove this when the rest will be ready + s_defaults.format.size.x = 1024; + s_defaults.format.size.y = 768; + s_defaults.format.colorFormat = CGxFormat::Fmt_Argb8888; + s_defaults.format.depthFormat = CGxFormat::Fmt_Ds248; RegisterGxCVars(); + ConsoleCommandRegister("gxRestart", &CCGxRestart, CATEGORY::GRAPHICS, nullptr); - // TODO ConsoleCommandRegister("gxRestart", &CCGxRestart, 1, nullptr); + // TODO: GxAdapterMonitorModes((int)&unk_CABCC8); + // TODO: ValidateFormatMonitor(&unk_CABDA8); - // TODO + // TODO: if ( GxAdapterDesktopMode(&v28) ) + if (true) { + s_requestedFormat.size.x = 1024; + s_requestedFormat.size.y = 768; + s_requestedFormat.colorFormat = CGxFormat::Fmt_Argb8888; + s_requestedFormat.depthFormat = CGxFormat::Fmt_Ds248; + } - // TODO - // - source the size values correctly - s_requestedFormat.size.x = 1024; - s_requestedFormat.size.y = 768; - s_requestedFormat.colorFormat = CGxFormat::Fmt_Argb8888; - s_requestedFormat.depthFormat = CGxFormat::Fmt_Ds248; + GxLog("ConsoleDeviceInitialize(): hwDetect = %d, hwChanged = %d", s_hwDetect, s_hwChanged); + + if (CmdLineGetBool(WOWCMD_RES_800x600)) { + s_requestedFormat.size.x = 800; + s_requestedFormat.size.y = 600; + } else if (CmdLineGetBool(WOWCMD_RES_1024x768)) { + s_requestedFormat.size.x = 1024; + s_requestedFormat.size.y = 768; + } else if (CmdLineGetBool(WOWCMD_RES_1280x960)) { + s_requestedFormat.size.x = 1280; + s_requestedFormat.size.y = 960; + } else if (CmdLineGetBool(WOWCMD_RES_1280x1024)) { + s_requestedFormat.size.x = 1280; + s_requestedFormat.size.y = 1024; + } else if (CmdLineGetBool(WOWCMD_RES_1600x1200)) { + s_requestedFormat.size.x = 1600; + s_requestedFormat.size.y = 1200; + } + + if (s_cvGxFixedFunction->GetInt() != 0) { + // TODO: (dword_CABD20 = 0) s_requestedFormat.unknown_field = 0; + s_requestedFormat.pos.y = 0; // <--- CHECK THIS + s_requestedFormat.pos.x = 0; + } if (s_hwDetect || s_hwChanged) { // TODO Sub76B3F0(&UnkCABAF0, &UnkCABB38); - // TODO s_cvFixedFunction->Set("0", 1, 0, 0, 1); + s_cvGxFixedFunction->Set("0", true, false, false, true); // TODO memcpy(&s_requestedFormat, &s_defaults.format, sizeof(s_requestedFormat)); s_requestedFormat.window = s_cvGxWindow->GetInt() != 0; @@ -268,17 +402,6 @@ void ConsoleDeviceInitialize(const char* title) { SetGxCVars(s_requestedFormat); } - // TODO - - // TODO s_requestedFormat.hwTnL = !CmdLineGetBool(CMD_SW_TNL); - s_requestedFormat.hwTnL = true; - - // TODO - - CGxFormat format; - memcpy(&format, &s_requestedFormat, sizeof(s_requestedFormat)); - - // Select gxApi based on user CVars and command-line parameters EGxApi api = GxApiDefault(); auto gxApiName = s_cvGxApi->GetString(); @@ -286,6 +409,12 @@ void ConsoleDeviceInitialize(const char* title) { auto gxOverride = CmdLineGetString(WOWCMD_GX_OVERRIDE); if (*gxOverride != '\0') { gxApiName = gxOverride; + } else if (CmdLineGetBool(CMD_OPENGL)) { + gxApiName = g_gxApiNames[GxApi_OpenGl]; + } else if (CmdLineGetBool(CMD_D3D)) { + gxApiName = g_gxApiNames[GxApi_D3d9]; + } else if (CmdLineGetBool(CMD_D3D9EX)) { + gxApiName = g_gxApiNames[GxApi_D3d9Ex]; } // Sanitize chosen gxApi against list of supported gxApis @@ -297,13 +426,28 @@ void ConsoleDeviceInitialize(const char* title) { } } - // Log - printf("GxApi_%s selected\n", g_gxApiNames[api]); + s_requestedFormat.fixLag = s_cvGxFixLag->GetInt() != 0; + s_requestedFormat.hwTnL = !CmdLineGetBool(CMD_SW_TNL); + + bool windowed = s_cvGxWindow->GetInt() != 0; + if (CmdLineGetBool(CMD_FULL_SCREEN)) { + windowed = false; + } else if (CmdLineGetBool(WOWCMD_WINDOWED)) { + windowed = true; + } + + s_requestedFormat.window = windowed; + // TODO: byte_CABD47 = windowed; + + GxLog("GxApi_%s selected\n", g_gxApiNames[api]); // Set internally (CVar value reflects the current gxApi at launch), // this will not Set() as CVar gxApi is latched s_cvGxApi->InternalSet(g_gxApiNames[api], true, false, false, true); + CGxFormat format; + memcpy(&format, &s_requestedFormat, sizeof(s_requestedFormat)); + CGxDevice* device = GxDevCreate(api, OsWindowProc, format); // TODO diff --git a/src/console/Handlers.cpp b/src/console/Handlers.cpp index 6bf03d5..80bdab9 100644 --- a/src/console/Handlers.cpp +++ b/src/console/Handlers.cpp @@ -4,6 +4,7 @@ #include "console/Command.hpp" #include "console/Screen.hpp" #include "event/Event.hpp" +#include "storm/Unicode.hpp" #include static int32_t s_historyIndex = 0; @@ -11,7 +12,7 @@ static int32_t s_historyIndex = 0; namespace { int32_t OnChar(const EVENT_DATA_CHAR* data, void* param) { - char character[2]; + char character[2] = {}; if (ConsoleAccessGetEnabled() && EventIsKeyDown(ConsoleGetHotKey())) { return 0; @@ -26,9 +27,6 @@ int32_t OnChar(const EVENT_DATA_CHAR* data, void* param) { return 0; } - // SUniSPutUTF8(data->ch, character); - - return 1; } diff --git a/src/event/win/Input.cpp b/src/event/win/Input.cpp index 687892b..619942f 100644 --- a/src/event/win/Input.cpp +++ b/src/event/win/Input.cpp @@ -8,6 +8,142 @@ #include "event/sdl/Input.hpp" #endif +static const uint32_t s_latin5lookup[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, + 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, + 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, + 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, + 0x7E, 0x7F, 0x20AC, 0x20, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, + 0x2021, 0x2C6, 0x2030, 0x160, 0x2039, 0x152, 0x20, 0x20, 0x20, + 0x20, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x2DC, + 0x2122, 0x161, 0x203A, 0x153, 0x20, 0x20, 0x178, 0x0A0, 0x0A1, + 0x0A2, 0x0A3, 0x0A4, 0x0A5, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0x0AA, + 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x0AF, 0x0B0, 0x0B1, 0x0B2, 0x0B3, + 0x0B4, 0x0B5, 0x0B6, 0x0B7, 0x0B8, 0x0B9, 0x0BA, 0x0BB, 0x0BC, + 0x0BD, 0x0BE, 0x0BF, 0x0C0, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, + 0x0C6, 0x0C7, 0x0C8, 0x0C9, 0x0CA, 0x0CB, 0x0CC, 0x0CD, 0x0CE, + 0x0CF, 0x11E, 0x0D1, 0x0D2, 0x0D3, 0x0D4, 0x0D5, 0x0D6, 0x0D7, + 0x0D8, 0x0D9, 0x0DA, 0x0DB, 0x0DC, 0x130, 0x15E, 0x0DF, 0x0E0, + 0x0E1, 0x0E2, 0x0E3, 0x0E4, 0x0E5, 0x0E6, 0x0E7, 0x0E8, 0x0E9, + 0x0EA, 0x0EB, 0x0EC, 0x0ED, 0x0EE, 0x0EF, 0x11F, 0x0F1, 0x0F2, + 0x0F3, 0x0F4, 0x0F5, 0x0F6, 0x0F7, 0x0F8, 0x0F9, 0x0FA, 0x0FB, + 0x0FC, 0x131, 0x15F, 0x0FF +}; + +static const uint32_t s_latin1lookup[32] = { + 0x0FFFE, 0x0FFFE, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x2C6, 0x2030, 0x160, 0x2039, 0x152, 0x0FFFE, 0x0FFFE, 0x0FFFE, + 0x0FFFE, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x2DC, 0x2122, 0x161, 0x203A, 0x153, 0x0FFFE, 0x0FFFE, 0x178 +}; + +static const uint32_t s_cyrilliclookup[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, + 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, + 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, + 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, + 0x7E, 0x7F, 0x402, 0x403, 0x201A, 0x453, 0x201E, 0x2026, 0x2020, + 0x2021, 0x20AC, 0x2030, 0x409, 0x2039, 0x40A, 0x40C, 0x40B, 0x40F, + 0x452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x20, + 0x2122, 0x459, 0x203A, 0x45A, 0x45C, 0x45B, 0x45F, 0x0A0, 0x40E, + 0x45E, 0x408, 0x0A4, 0x490, 0x0A6, 0x0A7, 0x401, 0x0A9, 0x404, + 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x407, 0x0B0, 0x0B1, 0x406, 0x456, + 0x491, 0x0B5, 0x0B6, 0x0B7, 0x451, 0x2116, 0x454, 0x0BB, 0x458, + 0x405, 0x455, 0x457, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, + 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, + 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, + 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x430, + 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, + 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, + 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, + 0x44C, 0x44D, 0x44E, 0x44F +}; + +static const uint32_t s_latin2lookup[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, + 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, + 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, + 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, + 0x7E, 0x7F, 0x20AC, 0x0FFFE, 0x201A, 0x0FFFE, 0x201E, 0x2026, 0x2020, + 0x2021, 0x0FFFE, 0x2030, 0x160, 0x2039, 0x15A, 0x164, 0x17D, 0x179, + 0x0FFFE, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0FFFE, + 0x2122, 0x161, 0x203A, 0x15B, 0x165, 0x17E, 0x17A, 0x0A0, 0x2C7, + 0x2D8, 0x141, 0x0A4, 0x104, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0x15E, + 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x17B, 0x0B0, 0x0B1, 0x2DB, 0x142, + 0x0B4, 0x0B5, 0x0B6, 0x0B7, 0x0B8, 0x105, 0x15F, 0x0BB, 0x13D, + 0x2DD, 0x13E, 0x17C, 0x154, 0x0C1, 0x0C2, 0x102, 0x0C4, 0x139, + 0x106, 0x0C7, 0x10C, 0x0C9, 0x118, 0x0CB, 0x11A, 0x0CD, 0x0CE, + 0x10E, 0x110, 0x143, 0x147, 0x0D3, 0x0D4, 0x150, 0x0D6, 0x0D7, + 0x158, 0x16E, 0x0DA, 0x170, 0x0DC, 0x0DD, 0x162, 0x0DF, 0x155, + 0x0E1, 0x0E2, 0x103, 0x0E4, 0x13A, 0x107, 0x0E7, 0x10D, 0x0E9, + 0x119, 0x0EB, 0x11B, 0x0ED, 0x0EE, 0x10F, 0x111, 0x144, 0x148, + 0x0F3, 0x0F4, 0x151, 0x0F6, 0x0F7, 0x159, 0x16F, 0x0FA, 0x171, + 0x0FC, 0x0FD, 0x163, 0x2D9 +}; + +static const uint32_t s_thailookup[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, + 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, + 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, + 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, + 0x7E, 0x7F, 0x20AC, 0x20, 0x20, 0x20, 0x20, 0x2026, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A0, 0x0E01, + 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, 0x0E08, 0x0E09, 0x0E0A, + 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, 0x0E10, 0x0E11, 0x0E12, 0x0E13, + 0x0E14, 0x0E15, 0x0E16, 0x0E17, 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, + 0x0E1D, 0x0E1E, 0x0E1F, 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, + 0x0E26, 0x0E27, 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, + 0x0E2F, 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, + 0x0E38, 0x0E39, 0x0E3A, 0x20, 0x20, 0x20, 0x20, 0x0E3F, 0x0E40, + 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, 0x0E48, 0x0E49, + 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, 0x0E50, 0x0E51, 0x0E52, + 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, + 0x20, 0x20, 0x20, 0x20 +}; + + static RECT s_defaultWindowRect; static int32_t s_savedResize; @@ -469,7 +605,13 @@ void OsInputGetMousePosition(int32_t* x, int32_t* y) { } } +uint32_t OsInputGetCodePage() { + return ::GetACP(); +} + int32_t OsWindowProc(void* window, uint32_t message, uintptr_t wparam, intptr_t lparam) { + static uint32_t s_codepage = 0; + auto hwnd = static_cast(window); // TODO @@ -539,11 +681,28 @@ int32_t OsWindowProc(void* window, uint32_t message, uintptr_t wparam, intptr_t uint32_t character = wparam; if (wparam >= 128) { - // TODO + if (s_codepage == 0) { + s_codepage = OsInputGetCodePage(); + } + if (s_codepage == 1254) { + // ANSI Turkish; Turkish (Windows) + character = s_latin5lookup[wparam % 256]; + } else if (s_codepage == 1252 && wparam < 0xA0) { + // ANSI Latin 1; Western European (Windows) + character = s_latin1lookup[wparam % 32]; + } else if (s_codepage == 1251) { + // ANSI Cyrillic; Cyrillic (Windows) + character = s_cyrilliclookup[wparam % 256]; + } else if (s_codepage == 1250) { + // ANSI Central European; Central European (Windows) + character = s_latin2lookup[wparam % 256]; + } else if (s_codepage == 874) { + // Thai (Windows) + character = s_thailookup[wparam % 256]; + } } OsQueuePut(OS_INPUT_CHAR, character, LOWORD(lparam), 0, 0); - return 0; } diff --git a/src/glue/CCharacterSelection.cpp b/src/glue/CCharacterSelection.cpp index e189f32..8ed69c5 100644 --- a/src/glue/CCharacterSelection.cpp +++ b/src/glue/CCharacterSelection.cpp @@ -1,9 +1,12 @@ #include "glue/CCharacterSelection.hpp" #include "model/CM2Shared.hpp" #include "ui/CSimpleModelFFX.hpp" +#include "client/ClientServices.hpp" +#include "net/Connection.hpp" TSGrowableArray CCharacterSelection::s_characterList; CSimpleModelFFX* CCharacterSelection::s_modelFrame; +float CCharacterSelection::s_charFacing = 0.0f; void CCharacterSelection::RenderPrep() { // TODO @@ -33,3 +36,34 @@ void CCharacterSelection::SetBackgroundModel(const char* modelPath) { model->IsDrawable(1, 1); } } + +void CCharacterSelection::SetCharFacing(float facing) { + // TODO: +} + +void CCharacterSelection::ClearCharacterList() { +} + +void CCharacterSelection::UpdateCharacterList() { + // TODO: ClearAddOnEnableState(0); + // TODO: Proper implementation + + auto& received = ClientServices::Connection()->m_characterList; + CCharacterSelection::s_characterList.SetCount(received.Count()); + for (uint32_t i = 0; i < received.Count(); ++i) { + CCharacterSelection::s_characterList[i].m_characterInfo = received[i]; + } + + if (CCharacterSelection::GetNumCharacters()) { + int32_t currentIndex = 0; + FrameScript_SignalEvent(8, "%d", currentIndex + 1); + } else { + int32_t currentIndex = 0; + FrameScript_SignalEvent(8, "%d", currentIndex + 1); + } + FrameScript_SignalEvent(7, nullptr); +} + +uint32_t CCharacterSelection::GetNumCharacters() { + return s_characterList.Count(); +} diff --git a/src/glue/CCharacterSelection.hpp b/src/glue/CCharacterSelection.hpp index 29dbdc7..7d25381 100644 --- a/src/glue/CCharacterSelection.hpp +++ b/src/glue/CCharacterSelection.hpp @@ -2,11 +2,12 @@ #define GLUE_C_CHARACTER_SELECTION_HPP #include +#include "net/Types.hpp" class CSimpleModelFFX; struct CharacterSelectionDisplay { - // TODO + CHARACTER_INFO m_characterInfo; }; class CCharacterSelection { @@ -14,10 +15,15 @@ class CCharacterSelection { // Static variables static TSGrowableArray s_characterList; static CSimpleModelFFX* s_modelFrame; + static float s_charFacing; // Static functions static void RenderPrep(); static void SetBackgroundModel(const char* modelPath); + static void SetCharFacing(float facing); + static void ClearCharacterList(); + static void UpdateCharacterList(); + static uint32_t GetNumCharacters(); }; #endif diff --git a/src/glue/CGlueMgr.cpp b/src/glue/CGlueMgr.cpp index 1061cde..e8a43fd 100644 --- a/src/glue/CGlueMgr.cpp +++ b/src/glue/CGlueMgr.cpp @@ -1,5 +1,6 @@ #include "glue/CGlueMgr.hpp" #include "glue/CRealmList.hpp" +#include "glue/CCharacterSelection.hpp" #include "client/Client.hpp" #include "client/ClientServices.hpp" #include "gx/Coordinate.hpp" @@ -43,6 +44,8 @@ unsigned char InterfaceKey[256] = { int32_t CGlueMgr::m_acceptedEULA = 1; // TODO int32_t CGlueMgr::m_acceptedTerminationWithoutNotice; int32_t CGlueMgr::m_acceptedTOS = 1; // TODO +int32_t CGlueMgr::m_processServerAlert = 1; +int32_t CGlueMgr::m_pendingTimerAlert; int32_t CGlueMgr::m_accountMsgAvailable; char CGlueMgr::m_accountName[1280]; float CGlueMgr::m_aspect; @@ -57,6 +60,7 @@ int32_t CGlueMgr::m_lastLoginResult; int32_t CGlueMgr::m_lastLoginState; int32_t CGlueMgr::m_loginResult; int32_t CGlueMgr::m_loginState; +int32_t CGlueMgr::m_matrixChallengeCount; int32_t CGlueMgr::m_matrixRemaining; int32_t CGlueMgr::m_reconnect; int32_t CGlueMgr::m_reload; @@ -67,6 +71,13 @@ int32_t CGlueMgr::m_showedDisconnect; CSimpleTop* CGlueMgr::m_simpleTop; int32_t CGlueMgr::m_suspended; +int32_t CGlueMgr::m_surveyTimer; +int32_t CGlueMgr::m_executedSurvey; +int32_t CGlueMgr::m_surveyDownload; +int32_t CGlueMgr::m_patchDownload; +bool CGlueMgr::m_deleteLocalPatch; + + float CalculateAspectRatio() { auto widescreenVar = CVar::Lookup("widescreen"); auto resolutionVar = CVar::Lookup("gxResolution"); @@ -197,9 +208,51 @@ int32_t CGlueMgr::HandleDisplaySizeChanged(const CSizeEvent& event) { return 1; } +void CGlueMgr::GetRealmList(bool showProgress) { + CGlueMgr::m_idleState = IDLE_REALM_LIST; + CGlueMgr::m_showedDisconnect = 0; + if (showProgress) { + auto text = FrameScript_GetText("REALM_LIST_IN_PROGRESS", -1, FRAMESCRIPT_GENDER::GENDER_NOT_APPLICABLE); + FrameScript_SignalEvent(3, "%s%s", "CANCEL", text); + } + ClientServices::GetRealmList(); +} + +void CGlueMgr::GetCharacterList() { + if (CGlueMgr::m_idleState != IDLE_WORLD_LOGIN) { + CGlueMgr::m_idleState = IDLE_CHARACTER_LIST; + auto text = FrameScript_GetText("CHAR_LIST_RETRIEVING", -1, GENDER_NOT_APPLICABLE); + FrameScript_SignalEvent(3, "%s%s", "CANCEL", text); + ClientServices::GetCharacterList(); + } +} + // TODO a1: const EVENT_DATA_IDLE* int32_t CGlueMgr::Idle(const void* a1, void* a2) { - // TODO + // TODO: + // if (gxDevice->IsStereoEnabled) { + // CGlueMgr::SetUIDepth(gxDevice->StereoGetConvergence); + // } + + auto loginConnection = ClientServices::LoginConnection(); + if (loginConnection) { + // Virtual call (loginConnection + 184) leads to nullsub + // Checked by tracing in debugger + } + + if (CGlueMgr::m_processServerAlert) { + // TODO: + // v2 = SStrLen("SERVERALERT:"); + // FrameScript_SignalEvent(0x15u, "%s", &CGlueMgr::m_serverAlert[(_DWORD)&v2[CGlueMgr::m_serverAlert[0] != -17 ? 0 : 3]]); + CGlueMgr::m_processServerAlert = 0; + } + + if (CGlueMgr::m_pendingTimerAlert) { + FrameScript_SignalEvent(0x21u, "%d", CGlueMgr::m_pendingTimerAlert); + CGlueMgr::m_pendingTimerAlert = 0; + } + + // TODO: CKBPage::UpdateLoadingQueue(); if (CGlueMgr::m_idleState == IDLE_NONE) { if (CGlueMgr::m_reload) { @@ -213,18 +266,17 @@ int32_t CGlueMgr::Idle(const void* a1, void* a2) { CGlueMgr::m_reload = 0; } - // TODO - // if (CGlueMgr::m_accountMsgAvailable) { - // FrameScript_SignalEvent(0x22u, 0); - // CGlueMgr::m_accountMsgAvailable = 0; - // } + if (CGlueMgr::m_accountMsgAvailable) { + FrameScript_SignalEvent(0x22u, 0); + CGlueMgr::m_accountMsgAvailable = 0; + } - // TODO sub_4D84A0(); + // TODO CGlueMgr::HandleBattlenetDisconnect(); return 1; } - // TODO + // TODO: LOOP { ConsoleWrite(GRUNT DEBUG MESSAGE) } WOWCS_OPS op; const char* msg; @@ -243,6 +295,25 @@ int32_t CGlueMgr::Idle(const void* a1, void* a2) { break; } + case IDLE_CHARACTER_LIST: { + CGlueMgr::PollCharacterList(errorCode, msg, complete, result, op); + break; + } + + case IDLE_12: { + if (CGlueMgr::m_patchDownload) { + CGlueMgr::PatchDownloadIdle(); + } else if (CGlueMgr::m_surveyDownload) { + CGlueMgr::SurveyDownloadIdle(); + } + break; + } + + case IDLE_13: { + CGlueMgr::PollUserSurvey(); + break; + } + // TODO other idle states default: @@ -366,47 +437,52 @@ void CGlueMgr::PollAccountLogin(int32_t errorCode, const char* msg, int32_t comp FrameScript_SignalEvent(4, "%s", msg); } - if (complete) { - if (result == 0) { - if (errorCode != 2) { - // TODO - } - - CGlueMgr::m_idleState = IDLE_NONE; - CGlueMgr::m_showedDisconnect = 0; - - if (errorCode == 2) { - // TODO CGlueMgr::m_disconnectPending = 1; - // TODO ClientServices::Connection()->Disconnect(); - } - - if (errorCode != 13) { - // TODO CCharacterSelection::ClearCharacterList(); - - if (ClientServices::GetInstance()->m_realmList.Count()) { - FrameScript_SignalEvent(5, nullptr); - CRealmList::UpdateList(); - } else { - // TODO - } - - return; - } - - if (!SStrCmpI(CGlueMgr::m_currentScreen, "charselect", STORM_MAX_STR)) { - CGlueMgr::SetScreen("login"); - return; - } - - return; - } - - if (op == COP_CONNECT) { - // TODO - - return; - } + if (!complete) { + return; } + + if (result == 0) { + if (errorCode != 2) { + // TODO + // Select Error Description with or without URL + } + + CGlueMgr::m_idleState = IDLE_NONE; + CGlueMgr::m_showedDisconnect = 0; + + if (errorCode == 2) { + CGlueMgr::m_disconnectPending = 1; + ClientServices::Connection()->Disconnect(); + } + + if (errorCode != 13) { + CCharacterSelection::ClearCharacterList(); + + if (ClientServices::GetInstance()->m_realmList.Count()) { + FrameScript_SignalEvent(5, nullptr); + CRealmList::UpdateList(); + } else { + CGlueMgr::GetRealmList(true); + } + + return; + } + + if (!SStrCmpI(CGlueMgr::m_currentScreen, "charselect", STORM_MAX_STR)) { + CGlueMgr::SetScreen("login"); + return; + } + + return; + } + + if (op == COP_CONNECT) { + // TODO: Correct arguments (they're not used inside method) + ClientServices::Connection()->AccountLogin("", "", 0, 0); + return; + } + + CGlueMgr::SetScreen("charselect"); } void CGlueMgr::PollLoginServerLogin() { @@ -426,32 +502,53 @@ void CGlueMgr::PollLoginServerLogin() { switch (CGlueMgr::m_loginState) { case LOGIN_STATE_FAILED: { - // TODO - + ClientServices::LoginConnection()->Logoff(); + CGlueMgr::m_idleState = IDLE_NONE; + CGlueMgr::m_showedDisconnect = 0; break; } case LOGIN_STATE_DOWNLOADFILE: { // TODO - + // Get String from Server's answer + // v14 = (char *)(ClientServices::LoginConnection() + 3928); + const char* v14 = ""; + if (!SStrCmpI(v14, "Patch", STORM_MAX_STR)) { + CGlueMgr::PatchDownloadStart(); + } + if (!SStrCmpI(v14, "Survey", STORM_MAX_STR)) { + CGlueMgr::SurveyDownloadStart(); + } break; } case LOGIN_STATE_PIN: { + FrameScript_SignalEvent(5, nullptr); // TODO - + // Calling GruntLogin::GetPinInfo + // v9 = (unsigned __int8 *)(*(int (__thiscall **)(int))(*(_DWORD *)v8 + 136))(v8); + int32_t v9[10] = {}; + FrameScript_SignalEvent(26, "%d%d%d%d%d%d%d%d%d%d", v9[0], v9[1], v9[2], v9[3], v9[4], v9[5], v9[6], v9[7], v9[8], v9[9]); + CGlueMgr::m_loginState = LOGIN_STATE_PIN_WAIT; break; } case LOGIN_STATE_MATRIX: { // TODO - + // Calling GruntLogin::GetMatrixInfo + // (*(void (__thiscall **)(int, int *, int *, int *, int *, unsigned __int8 *, _DWORD **))(*(_DWORD *)v10 + 160))(v10, &v73, &v69, &v72, &v70, &v77, &v75); + CGlueMgr::m_matrixChallengeCount = 0; + CGlueMgr::m_matrixRemaining = 0; + CGlueMgr::m_loginState = LOGIN_STATE_MATRIX_WAIT; + FrameScript_SignalEvent(5, 0); + // TODO: FrameScript_SignalEvent(0x1Cu, "%d%d%d%d%b%d", v73, v69, v72, v70, v77, v75); break; } case LOGIN_STATE_TOKEN: { - // TODO - + CGlueMgr::m_loginState = LOGIN_STATE_TOKEN_WAIT; + FrameScript_SignalEvent(5, 0); + FrameScript_SignalEvent(38, 0); break; } @@ -470,6 +567,58 @@ void CGlueMgr::PollLoginServerLogin() { } } +void CGlueMgr::PollCharacterList(int32_t errorCode, const char* msg, int32_t complete, int32_t result, WOWCS_OPS op) { + FrameScript_SignalEvent(4, "%s", msg); + + if (CGlueMgr::HandleBattlenetDisconnect()) { + CGlueMgr::m_idleState = IDLE_NONE; + CGlueMgr::m_showedDisconnect = 0; + } + + if (!complete) { + return; + } + + if (!result) { + if (errorCode == 2) { + CCharacterSelection::ClearCharacterList(); + CGlueMgr::GetRealmList(true); + } else { + FrameScript_SignalEvent(3, "%s%s", "OKAY", msg); + CGlueMgr::m_idleState = IDLE_NONE; + CGlueMgr::m_showedDisconnect = 0; + } + return; + } + + CGlueMgr::m_idleState = IDLE_NONE; + CGlueMgr::m_showedDisconnect = 0; + FrameScript_SignalEvent(5, nullptr); + CCharacterSelection::UpdateCharacterList(); + if (!CGlueMgr::m_accountMsgAvailable) { + return; + } + + FrameScript_SignalEvent(34, 0); + CGlueMgr::m_accountMsgAvailable = 0; +} + +void CGlueMgr::PollUserSurvey() { + if (CGlueMgr::m_surveyDownload && false /* virtual call */) { + if (CGlueMgr::m_executedSurvey) { + // TODO + } else { + CGlueMgr::m_executedSurvey = 1; + if (CGlueMgr::SurveyExecute()) { + auto text = FrameScript_GetText("LOGIN_STATE_SURVEY", -1, FRAMESCRIPT_GENDER::GENDER_NOT_APPLICABLE); + FrameScript_SignalEvent(3, "%s%s", "CANCEL", text); + CGlueMgr::SurveySendResults(); + // TODO: CGlueMgr::m_surveyTimer = OsGetAsyncTimeMs(); + } + } + } +} + void CGlueMgr::InitCursor() { uint32_t width; uint32_t height; @@ -664,13 +813,13 @@ void CGlueMgr::StatusDialogClick() { } case IDLE_ACCOUNT_LOGIN: - case IDLE_3: { + case IDLE_CHARACTER_LIST: { ClientServices::Connection()->Cancel(2); break; } - case IDLE_4: + case IDLE_REALM_LIST: case IDLE_5: case IDLE_6: case IDLE_10: { @@ -691,20 +840,21 @@ void CGlueMgr::StatusDialogClick() { break; } - case IDLE_11: { + case IDLE_WORLD_LOGIN: { CGlueMgr::m_showedDisconnect = 0; CGlueMgr::m_idleState = IDLE_NONE; - - // TODO - // CGlueMgr::GetCharacterList(); + CGlueMgr::GetCharacterList(); break; } case IDLE_12: case IDLE_13: { - // TODO - + if (CGlueMgr::m_surveyDownload) { + CGlueMgr::SurveyDownloadCancel(); + CGlueMgr::m_showedDisconnect = 0; + CGlueMgr::m_idleState = IDLE_NONE; + } break; } } @@ -744,3 +894,29 @@ void CGlueMgr::UpdateCurrentScreen(const char* screen) { // TODO } + +bool CGlueMgr::HandleBattlenetDisconnect() { + return false; +} + +void CGlueMgr::SurveyDownloadStart() { +} + +void CGlueMgr::SurveyDownloadCancel() { +} + +void CGlueMgr::SurveyDownloadIdle() { +} + +bool CGlueMgr::SurveyExecute() { + return false; +} + +void CGlueMgr::SurveySendResults() { +} + +void CGlueMgr::PatchDownloadStart() { +} + +void CGlueMgr::PatchDownloadIdle() { +} diff --git a/src/glue/CGlueMgr.hpp b/src/glue/CGlueMgr.hpp index a286768..627d3af 100644 --- a/src/glue/CGlueMgr.hpp +++ b/src/glue/CGlueMgr.hpp @@ -15,15 +15,15 @@ class CGlueMgr { IDLE_NONE = 0, IDLE_LOGIN_SERVER_LOGIN = 1, IDLE_ACCOUNT_LOGIN = 2, - IDLE_3 = 3, - IDLE_4 = 4, + IDLE_CHARACTER_LIST = 3, + IDLE_REALM_LIST = 4, IDLE_5 = 5, IDLE_6 = 6, IDLE_7 = 7, IDLE_8 = 8, IDLE_9 = 9, IDLE_10 = 10, - IDLE_11 = 11, + IDLE_WORLD_LOGIN = 11, IDLE_12 = 12, IDLE_13 = 13 }; @@ -32,6 +32,8 @@ class CGlueMgr { static int32_t m_acceptedEULA; static int32_t m_acceptedTerminationWithoutNotice; static int32_t m_acceptedTOS; + static int32_t m_processServerAlert; + static int32_t m_pendingTimerAlert; static int32_t m_accountMsgAvailable; static char m_accountName[]; static float m_aspect; @@ -46,6 +48,7 @@ class CGlueMgr { static int32_t m_lastLoginState; static int32_t m_loginResult; static int32_t m_loginState; + static int32_t m_matrixChallengeCount; static int32_t m_matrixRemaining; static int32_t m_reconnect; static int32_t m_reload; @@ -56,11 +59,19 @@ class CGlueMgr { static CSimpleTop* m_simpleTop; static int32_t m_suspended; + static int32_t m_surveyTimer; + static int32_t m_executedSurvey; + static int32_t m_surveyDownload; + static int32_t m_patchDownload; + static bool m_deleteLocalPatch; + // Static functions static void ChangeRealm(const REALM_INFO* realmInfo); static void DisplayLoginStatus(); // TODO a1: const EVENT_DATA_IDLE* static int32_t HandleDisplaySizeChanged(const CSizeEvent& event); + static void GetRealmList(bool showProgress); + static void GetCharacterList(); static int32_t Idle(const void* a1, void* a2); static void Initialize(); static void InitCursor(); @@ -68,6 +79,8 @@ class CGlueMgr { static void QuitGame(); static void PollAccountLogin(int32_t errorCode, const char* msg, int32_t complete, int32_t result, WOWCS_OPS op); static void PollLoginServerLogin(); + static void PollCharacterList(int32_t errorCode, const char* msg, int32_t complete, int32_t result, WOWCS_OPS op); + static void PollUserSurvey(); static void Resume(); static void SetCurrentAccount(const char* accountName); static void SetLoginStateAndResult(LOGIN_STATE state, LOGIN_RESULT result, char const* addrStr, char const* stateStr, char const* resultStr, uint8_t flags); @@ -76,6 +89,18 @@ class CGlueMgr { static void Sub4D8BA0(); static void Suspend(); static void UpdateCurrentScreen(const char* screen); + static bool HandleBattlenetDisconnect(); + + // Survey Download System + static void SurveyDownloadStart(); + static void SurveyDownloadCancel(); + static void SurveyDownloadIdle(); + static bool SurveyExecute(); + static void SurveySendResults(); + + // Patch Download System + static void PatchDownloadStart(); + static void PatchDownloadIdle(); }; #endif diff --git a/src/gx/CGxDevice.cpp b/src/gx/CGxDevice.cpp index 8e07f12..4af728a 100644 --- a/src/gx/CGxDevice.cpp +++ b/src/gx/CGxDevice.cpp @@ -106,6 +106,14 @@ uint32_t CGxDevice::s_texFormatBytesPerBlock[] = { CGxShader* CGxDevice::s_uiVertexShader = nullptr; CGxShader* CGxDevice::s_uiPixelShader = nullptr; +void CGxDevice::LogOpen() { + // TODO +} + +void CGxDevice::LogClose() { + // TODO +} + void CGxDevice::Log(const char* format, ...) { // TODO } @@ -333,7 +341,7 @@ void CGxDevice::ICursorDraw() { // Turn off everything GxRsSet(GxRs_PolygonOffset, 0); GxRsSet(GxRs_NormalizeNormals, 0); - GxRsSet(GxRs_BlendingMode, 1); + GxRsSet(GxRs_BlendingMode, GxBlend_AlphaKey); GxRsSetAlphaRef(); GxRsSet(GxRs_Lighting, 0); GxRsSet(GxRs_Fog, 0); @@ -355,6 +363,9 @@ void CGxDevice::ICursorDraw() { float cursorDepth = 1.0f; C44Matrix projection; + // Workaround for software cursor + // C44Matrix projection(2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0.002, 0, -1, -1, 0, 1); + // this->XformSetProjection(projection); if (!this->StereoEnabled() || (CGxDevice::s_uiVertexShader == 0 || !s_uiVertexShader->Valid()) || diff --git a/src/gx/CGxDevice.hpp b/src/gx/CGxDevice.hpp index 91eedf0..9cf9609 100644 --- a/src/gx/CGxDevice.hpp +++ b/src/gx/CGxDevice.hpp @@ -51,6 +51,8 @@ class CGxDevice { static CGxShader* s_uiPixelShader; // Static functions + static void LogOpen(); + static void LogClose(); static void Log(const char* format, ...); static void Log(const CGxFormat& format); #if defined(WHOA_SYSTEM_WIN) diff --git a/src/gx/CGxFormat.cpp b/src/gx/CGxFormat.cpp new file mode 100644 index 0000000..140daa9 --- /dev/null +++ b/src/gx/CGxFormat.cpp @@ -0,0 +1,50 @@ +#include "gx/CGxFormat.hpp" + +const char* CGxFormat::formatToColorBitsString[Formats_Last] = { "16", "24", "24", "30", "16", "24", "24", "32" }; + +CGxFormat::CGxFormat() { + this->size.x = 0; + this->size.y = 0; + this->pos.x = 0; + this->pos.y = 0; + this->sampleQuality = 0.0; + this->maximize = 0; + this->stereoEnabled = false; + this->sampleCount = 1; + this->aspect = 1; + this->unk1 = -1; + this->unk2 = -1; + this->unk3 = -1; + this->unk4 = -1; + this->unk5 = -1; + this->unk6 = -1; +} + +CGxFormat::CGxFormat(bool p_window, const C2iVector& p_size, Format p_colorFormat, Format p_depthFormat, uint32_t p_refreshRate, uint32_t p_vsync, bool p_hwTnl, bool p_fixLag, bool p_hwCursor, bool p_aspect, bool p_maximize) { + this->size.x = 0; + this->size.y = 0; + this->pos.x = 0; + this->pos.y = 0; + this->hwTnL = p_hwTnl; + this->hwCursor = p_hwCursor; + this->fixLag = p_fixLag; + this->window = p_window; + this->depthFormat = p_depthFormat; + this->size.x = p_size.x; + this->size.y = p_size.y; + this->sampleQuality = 0.0; + this->colorFormat = p_colorFormat; + this->refreshRate = p_refreshRate; + this->vsync = p_vsync; + this->aspect = p_aspect; + this->stereoEnabled = 0; + this->maximize = p_maximize; + this->backbuffers = 1; + this->sampleCount = 1; + this->unk1 = -1; + this->unk2 = -1; + this->unk3 = -1; + this->unk4 = -1; + this->unk5 = -1; + this->unk6 = -1; +} diff --git a/src/gx/CGxFormat.hpp b/src/gx/CGxFormat.hpp index 8960d6b..7f2960e 100644 --- a/src/gx/CGxFormat.hpp +++ b/src/gx/CGxFormat.hpp @@ -19,18 +19,34 @@ class CGxFormat { Formats_Last = 8 }; + CGxFormat(); + CGxFormat(bool p_window, const C2iVector& p_size, Format p_colorFormat, Format p_depthFormat, uint32_t p_refreshRate, uint32_t p_vsync, bool p_hwTnl, bool p_fixLag, bool p_hwCursor, bool p_aspect, bool p_maximize); + + static const char* formatToColorBitsString[Formats_Last]; + // Member variables + uint32_t apiSpecificModeID; bool hwTnL; bool hwCursor; bool fixLag; int8_t window; + int8_t aspect; int32_t maximize; Format depthFormat; C2iVector size; + uint32_t backbuffers; uint32_t sampleCount; + float sampleQuality; Format colorFormat; uint32_t refreshRate; uint32_t vsync; + bool stereoEnabled; + uint32_t unk1; + uint32_t unk2; + uint32_t unk3; + uint32_t unk4; + uint32_t unk5; + uint32_t unk6; C2iVector pos; }; diff --git a/src/gx/Gx.cpp b/src/gx/Gx.cpp index 07797f3..72a4ea9 100644 --- a/src/gx/Gx.cpp +++ b/src/gx/Gx.cpp @@ -59,3 +59,15 @@ void GxFormatColor(CImVector& color) { color = formattedColor; } } + +void GxLogOpen() { + CGxDevice::LogOpen(); +} + +void GxLogClose() { + CGxDevice::LogClose(); +} + +void GxLog(const char* format, ...) { + // TODO +} diff --git a/src/gx/Gx.hpp b/src/gx/Gx.hpp index 337090d..bd01c83 100644 --- a/src/gx/Gx.hpp +++ b/src/gx/Gx.hpp @@ -18,4 +18,10 @@ void GxCapsWindowSize(CRect&); void GxFormatColor(CImVector&); +void GxLogOpen(); + +void GxLogClose(); + +void GxLog(const char* format, ...); + #endif diff --git a/src/gx/d3d/CGxDeviceD3d.cpp b/src/gx/d3d/CGxDeviceD3d.cpp index b60fc97..12c621c 100644 --- a/src/gx/d3d/CGxDeviceD3d.cpp +++ b/src/gx/d3d/CGxDeviceD3d.cpp @@ -334,11 +334,17 @@ LRESULT CGxDeviceD3d::WindowProcD3d(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM case WM_SETCURSOR: { if (device) { - if (device->m_d3dDevice && lParam == 1) { + if (device->m_d3dDevice && LOWORD(lParam) == HTCLIENT) { SetCursor(nullptr); BOOL show = device->m_cursorVisible && device->m_hardwareCursor ? TRUE : FALSE; device->m_d3dDevice->ShowCursor(show); + } else { + // Uncomment when the "glove" cursor will be fixed + //break; } + } else { + // Uncomment when the "glove" cursor will be fixed + //break; } return 1; @@ -488,8 +494,7 @@ int32_t CGxDeviceD3d::DeviceSetFormat(const CGxFormat& format) { if (this->ICreateWindow(createFormat) && this->ICreateD3dDevice(createFormat) && this->CGxDevice::DeviceSetFormat(format)) { this->intF64 = 1; - - // TODO + this->m_hwCursorNeedsUpdate = 1; if (this->m_format.window == 0) { RECT windowRect; @@ -1102,6 +1107,17 @@ void CGxDeviceD3d::IRsSendToHw(EGxRenderState which) { break; } + case GxRs_Lighting: { + int32_t lightingEnable = 0; + + if (this->MasterEnable(GxMasterEnable_Lighting)) { + lightingEnable = static_cast(state->m_value); + } + + this->m_d3dDevice->SetRenderState(D3DRS_LIGHTING, lightingEnable); + break; + } + case GxRs_DepthTest: case GxRs_DepthFunc: { auto depthTest = static_cast((&this->m_appRenderStates[GxRs_DepthTest])->m_value); @@ -1249,6 +1265,11 @@ void CGxDeviceD3d::CursorSetVisible(int32_t visible) { } } +void CGxDeviceD3d::CursorUnlock(uint32_t x, uint32_t y) { + CGxDevice::CursorUnlock(x, y); + this->m_hwCursorNeedsUpdate = 1; +} + void CGxDeviceD3d::ICursorDraw() { if (!this->m_hardwareCursor) { this->ISceneBegin(); diff --git a/src/gx/d3d/CGxDeviceD3d.hpp b/src/gx/d3d/CGxDeviceD3d.hpp index 16c9f3e..ceeee09 100644 --- a/src/gx/d3d/CGxDeviceD3d.hpp +++ b/src/gx/d3d/CGxDeviceD3d.hpp @@ -252,6 +252,7 @@ class CGxDeviceD3d : public CGxDevice { virtual void ICursorDestroy(); virtual void ICursorDraw(); virtual void CursorSetVisible(int32_t visible); + virtual void CursorUnlock(uint32_t x, uint32_t y); virtual int32_t DeviceCreate(int32_t (*windowProc)(void* window, uint32_t message, uintptr_t wparam, intptr_t lparam), const CGxFormat& format); virtual int32_t DeviceSetFormat(const CGxFormat& format); virtual void* DeviceWindow(); diff --git a/src/gx/glsdl/GLSDLDevice.cpp b/src/gx/glsdl/GLSDLDevice.cpp index 81bfa15..6e67dac 100644 --- a/src/gx/glsdl/GLSDLDevice.cpp +++ b/src/gx/glsdl/GLSDLDevice.cpp @@ -1578,7 +1578,7 @@ GLTexture* GLSDLDevice::CreateTextureCubeMap(uint32_t size, uint32_t numMipMap, } void GLSDLDevice::Draw(GLEnum primitive, uint32_t a3, uint32_t a4) { - // TODO + this->GLSDLDraw(primitive, a3, a3 + a4, 0, 0, 0); } void GLSDLDevice::DrawIndexed(GLEnum primitive, uint32_t a3, uint32_t a4, uint32_t a5, uint32_t a6, uint32_t count) { diff --git a/src/net/Types.hpp b/src/net/Types.hpp index b70de69..fa3d356 100644 --- a/src/net/Types.hpp +++ b/src/net/Types.hpp @@ -1,6 +1,8 @@ #ifndef NET_TYPES_HPP #define NET_TYPES_HPP +#include + #include enum LOGIN_RESULT { @@ -1252,4 +1254,36 @@ struct REALM_INFO { uint16_t revision; }; +struct INVENTORY_ITEM { + uint8_t type; + uint32_t displayID; + uint32_t auraID; +}; + +struct CHARACTER_INFO { + uint64_t guid; + char name[48]; + uint32_t mapID; + uint32_t zoneID; + uint32_t guildID; + C3Vector position; + INVENTORY_ITEM items[23]; + uint32_t petDisplayInfoID; + uint32_t petExperienceLevel; + uint32_t petCreatureFamilyID; + uint32_t flags; + uint32_t customizeFlags; + uint8_t raceID; + uint8_t classID; + uint8_t sexID; + uint8_t skinID; + uint8_t faceID; + uint8_t hairStyleID; + uint8_t hairColorID; + uint8_t facialHairStyleID; + uint8_t experienceLevel; + uint8_t firstLogin; +}; + + #endif diff --git a/src/net/connection/ClientConnection.cpp b/src/net/connection/ClientConnection.cpp index 1c2d92e..64ca443 100644 --- a/src/net/connection/ClientConnection.cpp +++ b/src/net/connection/ClientConnection.cpp @@ -2,6 +2,7 @@ #include "net/Login.hpp" #include "client/ClientServices.hpp" #include "ui/FrameScript.hpp" +#include const char* s_errorCodeTokens[] = { "RESPONSE_SUCCESS", @@ -110,6 +111,13 @@ const char* s_errorCodeTokens[] = { "CHAR_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME", }; +void ClientConnection::AccountLogin(const char* name, const char* password, int32_t region, int32_t locale) { + STORM_ASSERT(this->m_statusComplete == 1); + STORM_ASSERT(name); + STORM_ASSERT(password); + this->Initiate(COP_AUTHENTICATE, 11, nullptr); +} + void ClientConnection::AccountLogin_Finish(int32_t errorCode) { this->Complete(errorCode == 12, errorCode); } @@ -124,6 +132,15 @@ void ClientConnection::AccountLogin_Queued() { // TODO CGlueMgr::UpdateWaitQueue(this->m_queuePosition); } +void ClientConnection::GetCharacterList() { + this->Initiate(COP_GET_CHARACTERS, 43, nullptr); + if (this->m_connected) { + this->RequestCharacterEnum(); + } else { + this->Cancel(4); + } +} + void ClientConnection::Cancel(int32_t errorCode) { this->Complete(0, errorCode); } diff --git a/src/net/connection/ClientConnection.hpp b/src/net/connection/ClientConnection.hpp index 2864ed0..9e7e866 100644 --- a/src/net/connection/ClientConnection.hpp +++ b/src/net/connection/ClientConnection.hpp @@ -18,16 +18,18 @@ class ClientConnection : public RealmConnection { // Virtual member functions virtual int32_t HandleConnect(); + virtual void Complete(int32_t result, int32_t errorCode); // Member functions ClientConnection(RealmResponse* realmResponse) : RealmConnection(realmResponse) {}; + void AccountLogin(const char* name, const char* password, int32_t region, int32_t locale); void AccountLogin_Finish(int32_t authResult); void AccountLogin_Queued(); + void GetCharacterList(); void Cancel(int32_t errorCode); void Cleanup(); - void Complete(int32_t result, int32_t errorCode); void Connect(); int32_t Disconnect(); void Initiate(WOWCS_OPS op, int32_t errorCode, void (*cleanup)()); diff --git a/src/net/connection/RealmConnection.cpp b/src/net/connection/RealmConnection.cpp index 64d5c48..983f3a3 100644 --- a/src/net/connection/RealmConnection.cpp +++ b/src/net/connection/RealmConnection.cpp @@ -24,7 +24,7 @@ int32_t RealmConnection::MessageHandler(void* param, NETMESSAGE msgId, uint32_t } case SMSG_ENUM_CHARACTERS_RESULT: { - // TODO + result = connection->HandleCharEnum(msgId, time, msg); break; } @@ -110,7 +110,7 @@ int32_t RealmConnection::HandleAuthChallenge(AuthenticationChallenge* challenge) // TODO switch to WDataStore CDataStore msg; - uint32_t localChallenge; + uint32_t localChallenge = 0; msg.Put(static_cast(CMSG_AUTH_SESSION)); @@ -193,6 +193,92 @@ int32_t RealmConnection::HandleAuthResponse(uint32_t msgId, uint32_t time, CData return 1; } +int32_t RealmConnection::HandleCharEnum(uint32_t msgId, uint32_t time, CDataStore* msg) { + if (this->m_realmResponse) { + this->m_realmResponse->GameServerResult(this, "SMSG_CHAR_ENUM", nullptr, nullptr); + } + + uint8_t count; + msg->Get(count); + + bool overflow = false; + if (count > 10) { + count = 0; + overflow = true; + } + + m_characterList.SetCount(count); + + for (uint32_t i = 0; i < count; ++i) { + auto& character = m_characterList[i]; + msg->Get(character.guid); + msg->GetString(character.name, 48); + msg->Get(character.raceID); + msg->Get(character.classID); + msg->Get(character.sexID); + msg->Get(character.skinID); + msg->Get(character.faceID); + msg->Get(character.hairStyleID); + msg->Get(character.hairColorID); + msg->Get(character.facialHairStyleID); + msg->Get(character.experienceLevel); + msg->Get(character.zoneID); + msg->Get(character.mapID); + msg->Get(character.position.x); + msg->Get(character.position.y); + msg->Get(character.position.z); + msg->Get(character.guildID); + msg->Get(character.flags); + msg->Get(character.customizeFlags); + msg->Get(character.firstLogin); + msg->Get(character.petDisplayInfoID); + msg->Get(character.petExperienceLevel); + msg->Get(character.petCreatureFamilyID); + for (uint32_t j = 0; j < 23; ++j) { + msg->Get(character.items[j].displayID); + msg->Get(character.items[j].type); + msg->Get(character.items[j].auraID); + } + } + + int32_t success = 0; + if (msg->IsRead()) { + if (!overflow) { + success = 1; + } + } else if (!overflow) { + // TODO: Proper implementation + uint32_t value; + msg->Get(value); + msg->Get(value); + msg->Get(value); + msg->Get(value); + msg->Get(value); + msg->Get(value); + msg->Get(value); + msg->Get(value); + msg->Get(value); + msg->Get(value); + if (msg->IsRead()) { + success = 1; + } + } + + if (!success) { + m_characterList.Clear(); + } + + this->m_realmResponse->CharacterListReceived(this, msg, success); + return 1; +} + void RealmConnection::SetSelectedRealm(uint32_t a2, uint32_t a3, uint32_t a4) { // TODO } + +void RealmConnection::RequestCharacterEnum() { + CDataStore msg; + msg.Put(static_cast(CMSG_ENUM_CHARACTERS)); + msg.Finalize(); + this->Send(&msg); +} diff --git a/src/net/connection/RealmConnection.hpp b/src/net/connection/RealmConnection.hpp index 5414dc2..28a8531 100644 --- a/src/net/connection/RealmConnection.hpp +++ b/src/net/connection/RealmConnection.hpp @@ -24,6 +24,7 @@ class RealmConnection : public NetClient { // Member variables RealmResponse* m_realmResponse; + TSFixedArray m_characterList; uint8_t m_authenticated = 0; uint32_t m_queuePosition = 0; uint32_t m_freeCharacterMigration = 0; @@ -34,11 +35,14 @@ class RealmConnection : public NetClient { // Virtual member functions virtual int32_t HandleAuthChallenge(AuthenticationChallenge* challenge); + virtual void Complete(int32_t result, int32_t errorCode) = 0; // Member functions RealmConnection(RealmResponse* realmResponse); int32_t HandleAuthResponse(uint32_t msgId, uint32_t time, CDataStore* msg); + int32_t HandleCharEnum(uint32_t msgId, uint32_t time, CDataStore* msg); void SetSelectedRealm(uint32_t a2, uint32_t a3, uint32_t a4); + void RequestCharacterEnum(); }; #endif diff --git a/src/net/connection/RealmResponse.hpp b/src/net/connection/RealmResponse.hpp index 6600312..16fe499 100644 --- a/src/net/connection/RealmResponse.hpp +++ b/src/net/connection/RealmResponse.hpp @@ -5,6 +5,7 @@ class RealmResponse { public: // Virtual member functions virtual void HandleAuthResponse(RealmConnection* connection, uint8_t authResult) = 0; + virtual void CharacterListReceived(RealmConnection* connection, void* a2, int32_t success) = 0; virtual void GameServerResult(RealmConnection* connection, const char* a3, const char* a4, const char* a5) = 0; }; diff --git a/src/ui/CBackdropGenerator.cpp b/src/ui/CBackdropGenerator.cpp index 5be5ac0..467b472 100644 --- a/src/ui/CBackdropGenerator.cpp +++ b/src/ui/CBackdropGenerator.cpp @@ -152,6 +152,13 @@ void CBackdropGenerator::LoadXML(XMLNode* node, CStatus* status) { } } +void CBackdropGenerator::SetVertexColor(const CImVector& color) { + this->m_color = color; + if (this->m_backgroundTexture) { + this->m_backgroundTexture->SetVertexColor(color); + } +} + void CBackdropGenerator::SetBorderVertexColor(const CImVector& borderColor) { this->m_borderColor = borderColor; diff --git a/src/ui/CBackdropGenerator.hpp b/src/ui/CBackdropGenerator.hpp index 06f248e..ce9fca6 100644 --- a/src/ui/CBackdropGenerator.hpp +++ b/src/ui/CBackdropGenerator.hpp @@ -42,6 +42,7 @@ class CBackdropGenerator { CBackdropGenerator(); void Generate(const CRect* rect); void LoadXML(XMLNode* node, CStatus* status); + void SetVertexColor(const CImVector& color); void SetBorderVertexColor(const CImVector& borderColor); void SetOutput(CSimpleFrame* frame); }; diff --git a/src/ui/CLayoutFrame.cpp b/src/ui/CLayoutFrame.cpp index 3025ca3..bea0ec0 100644 --- a/src/ui/CLayoutFrame.cpp +++ b/src/ui/CLayoutFrame.cpp @@ -11,10 +11,10 @@ STORM_EXPLICIT_LIST(CLayoutFrame, resizeLink) LayoutFrame::s_resizePendingList; float SynthesizeSide(float center, float opposite, float size) { - if (center != CFramePoint::UNDEFINED && opposite != CFramePoint::UNDEFINED) { - return center + center - opposite; - } else if (opposite != CFramePoint::UNDEFINED && size != 0.0f) { + if (opposite != CFramePoint::UNDEFINED && size != 0.0f) { return opposite + size; + } else if (center != CFramePoint::UNDEFINED && opposite != CFramePoint::UNDEFINED) { + return center + center - opposite; } else if (center != CFramePoint::UNDEFINED && size != 0.0f) { return center + (size * 0.5f); } else { diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index eafbb87..9c10673 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -12,6 +12,7 @@ target_include_directories(ui target_link_libraries(ui PRIVATE client + clientobject console db event diff --git a/src/ui/CSimpleEditBox.cpp b/src/ui/CSimpleEditBox.cpp index 6945ce9..5f0b9ae 100644 --- a/src/ui/CSimpleEditBox.cpp +++ b/src/ui/CSimpleEditBox.cpp @@ -871,6 +871,11 @@ int32_t CSimpleEditBox::OnLayerKeyDown(const CKeyEvent& evt) { return 1; } + case KEY_TAB: { + // TODO correct implementation + this->RunOnTabPressedScript(); + } + // TODO // - remaining keys @@ -994,6 +999,12 @@ void CSimpleEditBox::RunOnEnterPressedScript() { } } +void CSimpleEditBox::RunOnTabPressedScript() { + if (this->m_onTabPressed.luaRef) { + this->RunScript(this->m_onTabPressed, 0, 0); + } +} + void CSimpleEditBox::RunOnTextChangedScript(int32_t changed) { if (this->m_onTextChanged.luaRef) { auto L = FrameScript_GetContext(); diff --git a/src/ui/CSimpleEditBox.hpp b/src/ui/CSimpleEditBox.hpp index 5fb7e26..2c264d8 100644 --- a/src/ui/CSimpleEditBox.hpp +++ b/src/ui/CSimpleEditBox.hpp @@ -98,6 +98,7 @@ class CSimpleEditBox : public CSimpleFrame, CSimpleFontedFrame { void RunOnEditFocusGainedScript(); void RunOnEditFocusLostScript(); void RunOnEnterPressedScript(); + void RunOnTabPressedScript(); void RunOnTextChangedScript(int32_t changed); void SetCursorPosition(int32_t position); void SetHistoryLines(int32_t a2); diff --git a/src/ui/CSimpleEditBoxScript.cpp b/src/ui/CSimpleEditBoxScript.cpp index 7b98f94..d24ad96 100644 --- a/src/ui/CSimpleEditBoxScript.cpp +++ b/src/ui/CSimpleEditBoxScript.cpp @@ -176,11 +176,34 @@ int32_t CSimpleEditBox_GetTextInsets(lua_State* L) { } int32_t CSimpleEditBox_SetFocus(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (lua_type(L, 1) != LUA_TTABLE) { + luaL_error(L, "Attempt to find 'this' in non-table object (used '.' instead of ':' ?)"); + } + + lua_rawgeti(L, 1, 0); + auto object = reinterpret_cast(lua_touserdata(L, -1)); + lua_settop(L, -2); + + STORM_ASSERT(object); + + CSimpleEditBox::SetKeyboardFocus(object); + return 0; } int32_t CSimpleEditBox_ClearFocus(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (lua_type(L, 1) != LUA_TTABLE) { + luaL_error(L, "Attempt to find 'this' in non-table object (used '.' instead of ':' ?)"); + } + + lua_rawgeti(L, 1, 0); + auto object = reinterpret_cast(lua_touserdata(L, -1)); + lua_settop(L, -2); + + STORM_ASSERT(object); + + // TODO + // CSimpleEditBox::ClearKeyboardFocus(object); + return 0; } int32_t CSimpleEditBox_HasFocus(lua_State* L) { diff --git a/src/ui/CSimpleFontStringScript.cpp b/src/ui/CSimpleFontStringScript.cpp index f789928..247918b 100644 --- a/src/ui/CSimpleFontStringScript.cpp +++ b/src/ui/CSimpleFontStringScript.cpp @@ -4,6 +4,8 @@ #include "util/Lua.hpp" #include "util/Unimplemented.hpp" #include +#include + int32_t CSimpleFontString_IsObjectType(lua_State* L) { WHOA_UNIMPLEMENTED(0); @@ -132,7 +134,24 @@ int32_t CSimpleFontString_SetText(lua_State* L) { } int32_t CSimpleFontString_SetFormattedText(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (lua_type(L, 1) != LUA_TTABLE) { + luaL_error(L, "Attempt to find 'this' in non-table object (used '.' instead of ':' ?)"); + return 0; + } + + auto type = CSimpleFontString::GetObjectType(); + auto string = static_cast(FrameScript_GetObjectThis(L, type)); + + if (!string->m_font) { + luaL_error(L, "%s:SetText(): Font not set", string->GetDisplayName()); + return 0; + } + + char text[2048] = {}; + FrameScript_Sprintf(L, 2, text, sizeof(text)); + string->SetText(text, 0); + + return 0; } int32_t CSimpleFontString_GetTextColor(lua_State* L) { diff --git a/src/ui/CSimpleFrame.cpp b/src/ui/CSimpleFrame.cpp index f0ef022..6aa167a 100644 --- a/src/ui/CSimpleFrame.cpp +++ b/src/ui/CSimpleFrame.cpp @@ -1327,7 +1327,19 @@ void CSimpleFrame::SetBeingScrolled(int32_t a2, int32_t a3) { } void CSimpleFrame::SetFrameAlpha(uint8_t alpha) { - // TODO + if (this->m_alpha == alpha) { + return; + } + + this->m_alpha = alpha; + + for (auto region = this->m_regions.Head(); region; region = this->m_regions.Link(region)->Next()) { + region->OnColorChanged(true); + } + + for (auto child = this->m_children.Head(); child; child = this->m_children.Link(child)->Next()) { + child->frame->SetFrameAlpha(alpha); + } } void CSimpleFrame::SetFrameFlag(int32_t flag, int32_t on) { diff --git a/src/ui/CSimpleFrame.hpp b/src/ui/CSimpleFrame.hpp index 02eb4ab..86fd306 100644 --- a/src/ui/CSimpleFrame.hpp +++ b/src/ui/CSimpleFrame.hpp @@ -37,6 +37,7 @@ class CSimpleFrame : public CScriptRegion { float m_depth = 0.0; FRAME_STRATA m_strata = FRAME_STRATA_MEDIUM; int32_t m_level = 0; + uint8_t m_alpha = 255; uint32_t m_eventmask = 0; int32_t m_shown = 0; int32_t m_visible = 0; diff --git a/src/ui/CSimpleFrameScript.cpp b/src/ui/CSimpleFrameScript.cpp index dd9e075..470035e 100644 --- a/src/ui/CSimpleFrameScript.cpp +++ b/src/ui/CSimpleFrameScript.cpp @@ -2,6 +2,7 @@ #include "gx/Coordinate.hpp" #include "ui/CSimpleFrame.hpp" #include "ui/FrameScript.hpp" +#include "ui/CBackdropGenerator.hpp" #include "util/Lua.hpp" #include "util/Unimplemented.hpp" #include @@ -460,7 +461,28 @@ int32_t CSimpleFrame_GetBackdropColor(lua_State* L) { } int32_t CSimpleFrame_SetBackdropColor(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (lua_type(L, 1) != LUA_TTABLE) { + luaL_error(L, "Attempt to find 'this' in non-table object (used '.' instead of ':' ?)"); + } + + lua_rawgeti(L, 1, 0); + auto object = reinterpret_cast(lua_touserdata(L, -1)); + lua_settop(L, -2); + + STORM_ASSERT(object); + + CImVector color; + auto red = lua_tonumber(L, 2); + auto green = lua_tonumber(L, 3); + auto blue = lua_tonumber(L, 4); + auto alpha = lua_isnumber(L, 5) ? lua_tonumber(L, 5) : 1.0; + color.Set(alpha, red, green, blue); + + if (object->m_backdrop) { + object->m_backdrop->SetVertexColor(color); + } + + return 0; } int32_t CSimpleFrame_GetBackdropBorderColor(lua_State* L) { @@ -468,7 +490,28 @@ int32_t CSimpleFrame_GetBackdropBorderColor(lua_State* L) { } int32_t CSimpleFrame_SetBackdropBorderColor(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (lua_type(L, 1) != LUA_TTABLE) { + luaL_error(L, "Attempt to find 'this' in non-table object (used '.' instead of ':' ?)"); + } + + lua_rawgeti(L, 1, 0); + auto object = reinterpret_cast(lua_touserdata(L, -1)); + lua_settop(L, -2); + + STORM_ASSERT(object); + + CImVector color; + auto red = lua_tonumber(L, 2); + auto green = lua_tonumber(L, 3); + auto blue = lua_tonumber(L, 4); + auto alpha = lua_isnumber(L, 5) ? lua_tonumber(L, 5) : 1.0; + color.Set(alpha, red, green, blue); + + if (object->m_backdrop) { + object->m_backdrop->SetBorderVertexColor(color); + } + + return 0; } int32_t CSimpleFrame_SetDepth(lua_State* L) { diff --git a/src/ui/FrameScript.cpp b/src/ui/FrameScript.cpp index 558c517..fb51ca6 100644 --- a/src/ui/FrameScript.cpp +++ b/src/ui/FrameScript.cpp @@ -10,6 +10,7 @@ #include #include #include +#include const char* g_glueScriptEvents[41]; const char* g_scriptEvents[722]; @@ -867,6 +868,216 @@ void FrameScript_UnregisterScriptEvent(FrameScript_Object* object, FrameScript_E } } +static void addchar(char* buffer, size_t bufferSize, char ch) { + auto length = SStrLen(buffer); + if (length + 1 < bufferSize) + { + buffer[length++] = ch; + buffer[length] = '\0'; + } +} + +static void addstring(char* buffer, size_t bufferSize, const char* source) { + uint32_t dsize = 0; + uint32_t size = 0; + + dsize = SStrLen(buffer); + size = SStrLen(source); + + if (dsize + size >= bufferSize) { + size = bufferSize - dsize; + // Check for space for trailing zero + if (size < 2) { + size = 0; + } else { + size--; + } + } + + if (size > 0) + memmove(&buffer[dsize], source, size); + + buffer[dsize + size] = '\0'; +} + +static void addstring(char* buffer, size_t bufferSize, const char* source, size_t count) { + uint32_t dsize = 0; + uint32_t size = 0; + + dsize = SStrLen(buffer); + size = std::min(SStrLen(source), count); + + if (dsize + size >= bufferSize) { + size = bufferSize - dsize; + // Check for space for trailing zero + if (size < 2) { + size = 0; + } else { + size--; + } + } + + if (size > 0) + memmove(&buffer[dsize], source, size); + + buffer[dsize + size] = '\0'; +} + +static void addquoted(lua_State* L, char* buffer, size_t bufferSize, int arg) { + size_t l; + const char* s = luaL_checklstring(L, arg, &l); + addchar(buffer, bufferSize, '"'); + while (l--) { + switch (*s) { + case '"': + case '\\': + case '\n': { + addchar(buffer, bufferSize, '\\'); + addchar(buffer, bufferSize, *s); + break; + } + case '\r': { + addstring(buffer, bufferSize, "\\r"); + break; + } + case '\0': { + addstring(buffer, bufferSize, "\\000"); + break; + } + default: { + addchar(buffer, bufferSize, *s); + break; + } + } + s++; + } + addchar(buffer, bufferSize, '"'); +} + +#define FORMAT_FLAGS "-+ #0" + +static const char* scanformat(lua_State* L, const char* strfrmt, char* form) { + const char* flags = "-+ #0"; + const char* p = strfrmt; + + while (*p != '\0' && SStrChrR(FORMAT_FLAGS, *p) != NULL) { + p++; /* skip flags */ + } + + if ((size_t)(p - strfrmt) >= sizeof(FORMAT_FLAGS)) { + luaL_error(L, "invalid format (repeated flags)"); + } + + if (isdigit((unsigned char)(*p))) { + p++; /* skip width */ + } + + if (isdigit((unsigned char)(*p))) { + p++; /* (2 digits at most) */ + } + + if (*p == '.') { + p++; + if (isdigit((unsigned char)(*p))) { + p++; /* skip precision */ + } + if (isdigit((unsigned char)(*p))) { + p++; /* (2 digits at most) */ + } + } + if (isdigit((unsigned char)(*p))) { + luaL_error(L, "invalid format (width or precision too long)"); + } + + *(form++) = '%'; + strncpy(form, strfrmt, p - strfrmt + 1); + form += p - strfrmt + 1; + *form = '\0'; + return p; +} + +static void addintlen(char* form) { + size_t l = SStrLen(form); + char spec = form[l - 1]; + strcpy(form + l - 1, LUA_INTFRMLEN); + form[l + sizeof(LUA_INTFRMLEN) - 2] = spec; + form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0'; +} + +void FrameScript_Sprintf(lua_State* L, int startIndex, char* buffer, uint32_t bufferSize) { + // maximum size of each formatted item (> len(format('%99.99f', -1e308))) + const size_t MAX_ITEM = 512; + + // maximum size of each format specification (such as '%-099.99d') + // (+10 accounts for %99.99x plus margin of error) + const size_t MAX_FORMAT = sizeof(FORMAT_FLAGS) + sizeof(LUA_INTFRMLEN) + 10; + + int arg = startIndex; + size_t sfl; + const char* strfrmt = luaL_checklstring(L, arg, &sfl); + const char* strfrmt_end = strfrmt + sfl; + while (strfrmt < strfrmt_end) { + if (*strfrmt != '%') { + addchar(buffer, bufferSize, *strfrmt++); + } else if (*++strfrmt == '%') { + addchar(buffer, bufferSize, *strfrmt++); /* %% */ + } else { /* format item */ + char form[MAX_FORMAT]; /* to store the format (`%...') */ + char buff[MAX_ITEM]; /* to store the formatted item */ + arg++; + strfrmt = scanformat(L, strfrmt, form); + switch (*strfrmt++) { + case 'c': { + sprintf(buff, form, (int)luaL_checknumber(L, arg)); + break; + } + case 'd': + case 'i': { + addintlen(form); + sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg)); + break; + } + case 'o': + case 'u': + case 'x': + case 'X': { + addintlen(form); + sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg)); + break; + } + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': { + sprintf(buff, form, (double)luaL_checknumber(L, arg)); + break; + } + case 'q': { + addquoted(L, buffer, bufferSize, arg); + continue; /* skip the 'addsize' at the end */ + } + case 's': { + size_t l; + const char* s = luaL_checklstring(L, arg, &l); + if (!strchr(form, '.') && l >= 100) { + /* no precision and string is too long to be formatted; + keep original string */ + continue; /* skip the `addsize' at the end */ + } else { + sprintf(buff, form, s); + break; + } + } + default: { /* also treat cases `pnLlh' */ + luaL_error(L, "invalid option " LUA_QL("%%%c") " to " LUA_QL("format"), *(strfrmt - 1)); + } + } + addstring(buffer, bufferSize, buff); + } + } +} + void GlueScriptEventsInitialize() { g_glueScriptEvents[0] = "SET_GLUE_SCREEN"; g_glueScriptEvents[1] = "START_GLUE_MUSIC"; diff --git a/src/ui/FrameScript.hpp b/src/ui/FrameScript.hpp index debf40f..6221927 100644 --- a/src/ui/FrameScript.hpp +++ b/src/ui/FrameScript.hpp @@ -100,6 +100,8 @@ void FrameScript_SignalEvent(uint32_t index, const char* format, ...); void FrameScript_UnregisterScriptEvent(FrameScript_Object* object, FrameScript_EventObject* event); +void FrameScript_Sprintf(lua_State* L, int startIndex, char* buffer, uint32_t bufferSize); + void GlueScriptEventsInitialize(); void ScriptEventsInitialize(); diff --git a/src/ui/ScriptFunctionsCharSelect.cpp b/src/ui/ScriptFunctionsCharSelect.cpp index 365aa78..51d464f 100644 --- a/src/ui/ScriptFunctionsCharSelect.cpp +++ b/src/ui/ScriptFunctionsCharSelect.cpp @@ -5,7 +5,9 @@ #include "ui/Types.hpp" #include "util/Lua.hpp" #include "util/Unimplemented.hpp" -#include +#include "glue/CGlueMgr.hpp" +#include "db/Db.hpp" +#include "clientobject/Unit_C.hpp" int32_t Script_SetCharSelectModelFrame(lua_State* L) { if (!lua_isstring(L, 1)) { @@ -35,17 +37,74 @@ int32_t Script_SetCharSelectBackground(lua_State* L) { } int32_t Script_GetCharacterListUpdate(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + // TODO: CCharSelectInfo::ClearCharacterModel(); + // TODO: CCharSelectInfo::ClearPetModel(); + CGlueMgr::GetCharacterList(); + return 0; } int32_t Script_GetNumCharacters(lua_State* L) { - lua_pushnumber(L, CCharacterSelection::s_characterList.Count()); + lua_pushnumber(L, CCharacterSelection::GetNumCharacters()); return 1; } int32_t Script_GetCharacterInfo(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isnumber(L, 1)) { + luaL_error(L, "Usage: GetCharacterInfo(index)"); + } + + int index = static_cast(lua_tonumber(L, 1)) - 1; + if (index < 0 || index > CCharacterSelection::GetNumCharacters()) { + lua_pushnil(L); // name + lua_pushnil(L); // race + lua_pushnil(L); // class + lua_pushnumber(L, 0.0); // level + lua_pushnumber(L, 0.0); // zone + lua_pushnil(L); // sex + lua_pushnil(L); // ghost + lua_pushnil(L); // PCC + lua_pushnil(L); // PRC + lua_pushnil(L); // PFC + return 10; + } + + auto& character = CCharacterSelection::s_characterList[index].m_characterInfo; + lua_pushstring(L, character.name); + + auto raceName = CGUnit_C::GetDisplayRaceNameFromRecord(g_chrRacesDB.GetRecord(character.raceID), character.sexID); + lua_pushstring(L, raceName ? raceName : ""); + + // TODO: auto className = CGUnit_C::GetDisplayClassNameFromRecord(g_chrClassesDB.GetRecord(character.classID), character.sexID); + auto className = "Warrior"; + lua_pushstring(L, className ? className : ""); + + lua_pushnumber(L, character.experienceLevel); + + // TODO: auto areaRecord = g_areaTableDB.GetRecord(character.zoneID); + void* areaRecord = nullptr; + if (areaRecord) { + // TODO: lua_pushstring(L, areaRecord->name) + } else { + lua_pushnil(L); + } + + // TODO: Use g_glueFrameScriptGenders[character.sexID] + lua_pushnumber(L, 0); + + // ghost + lua_pushboolean(L, character.flags & 0x2000); + + // PCC + lua_pushboolean(L, character.customizeFlags & 1); + + // PRC + lua_pushboolean(L, character.customizeFlags & 0x100000); + + // PFC + lua_pushboolean(L, character.customizeFlags & 0x10000); + + return 10; } int32_t Script_SelectCharacter(lua_State* L) { @@ -71,11 +130,19 @@ int32_t Script_UpdateSelectionCustomizationScene(lua_State* L) { } int32_t Script_GetCharacterSelectFacing(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + // Radian to Degree + lua_pushnumber(L, CCharacterSelection::s_charFacing * 57.29578f); + return 1; } int32_t Script_SetCharacterSelectFacing(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isnumber(L, 1)) { + luaL_error(L, "Usage: SetCharacterSelectFacing(degrees)"); + } + // Degree to Radian + float facing = lua_tonumber(L, 1) * 0.017453292; + CCharacterSelection::SetCharFacing(facing); + return 1; } int32_t Script_GetSelectBackgroundModel(lua_State* L) { @@ -83,16 +150,23 @@ int32_t Script_GetSelectBackgroundModel(lua_State* L) { return luaL_error(L, "Usage: GetSelectBackgroundModel(index)"); } - auto characterIndex = static_cast(lua_tonumber(L, 1)) - 1; + auto index = static_cast(lua_tonumber(L, 1)) - 1; + + // TODO: if (SFile::IsTrial()) + if (false) { + lua_pushstring(L, "CharacterSelect"); + return 1; + } // TODO ChrRacesRec* racesRec = nullptr; - if (characterIndex < 0 || characterIndex >= CCharacterSelection::s_characterList.Count()) { + if (index < 0 || index >= CCharacterSelection::s_characterList.Count()) { racesRec = g_chrRacesDB.GetRecord(2); } else { - // TODO + auto raceID = CCharacterSelection::s_characterList[index].m_characterInfo.raceID; + racesRec = g_chrRacesDB.GetRecord(raceID); } if (racesRec) { diff --git a/src/ui/ScriptFunctionsGlueScriptEvents.cpp b/src/ui/ScriptFunctionsGlueScriptEvents.cpp index 376524a..8b7fcf7 100644 --- a/src/ui/ScriptFunctionsGlueScriptEvents.cpp +++ b/src/ui/ScriptFunctionsGlueScriptEvents.cpp @@ -5,6 +5,7 @@ #include "glue/CGlueMgr.hpp" #include "gx/Coordinate.hpp" #include "net/connection/ClientConnection.hpp" +#include "net/Login.hpp" #include "ui/CSimpleTop.hpp" #include "ui/Types.hpp" #include "console/CVar.hpp" @@ -19,7 +20,15 @@ int32_t Script_IsShiftKeyDown(lua_State* L) { } int32_t Script_GetBuildInfo(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + auto szVersion = FrameScript_GetText("VERSION", -1, GENDER_NOT_APPLICABLE); + auto szVersionType = FrameScript_GetText("RELEASE_BUILD", -1, GENDER_NOT_APPLICABLE); + + lua_pushstring(L, szVersion); + lua_pushstring(L, szVersionType); + lua_pushstring(L, "3.3.5"); + lua_pushstring(L, "12340"); + lua_pushstring(L, "Jun 24 2010"); + return 5; } int32_t Script_GetLocale(lua_State* L) { @@ -27,7 +36,9 @@ int32_t Script_GetLocale(lua_State* L) { } int32_t Script_GetSavedAccountName(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + // TODO + lua_pushstring(L, ""); + return 1; } int32_t Script_SetSavedAccountName(lua_State* L) { @@ -264,7 +275,12 @@ int32_t Script_GetServerName(lua_State* L) { } int32_t Script_DisconnectFromServer(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (ClientServices::Connection()->IsConnected()) { + CGlueMgr::m_disconnectPending = 1; + ClientServices::Connection()->Disconnect(); + } + ClientServices::LoginConnection()->Logoff(); + return 0; } int32_t Script_IsConnectedToServer(lua_State* L) { diff --git a/src/util/SFile.cpp b/src/util/SFile.cpp index 84a29f6..5cc0d76 100644 --- a/src/util/SFile.cpp +++ b/src/util/SFile.cpp @@ -8,8 +8,9 @@ #include #include "util/Filesystem.hpp" -static char s_basepath[STORM_MAX_PATH] = {0}; -static char s_datapath[STORM_MAX_PATH] = {0}; +static char s_basepath[STORM_MAX_PATH] = { 0 }; +static char s_datapath[STORM_MAX_PATH] = { 0 }; +static char s_datapath2[STORM_MAX_PATH] = { 0 }; // TODO Proper implementation int32_t SFile::Close(SFile* file) { @@ -279,3 +280,17 @@ int32_t SFile::GetDataPath(char* buffer, size_t bufferchars) { SStrCopy(buffer, s_datapath, bufferchars); return 1; } + +int32_t SFile::SetDataPathAlternate(const char* path) { + SStrCopy(s_datapath2, path, STORM_MAX_PATH); + size_t length = SStrLen(s_datapath2); + if (length && s_datapath2[length - 1] != '\\' && s_datapath2[length - 1] != '/') { + SStrPack(s_datapath2, "\\", STORM_MAX_PATH); + } + return 1; +} + +int32_t SFile::RebuildHash() { + // TODO + return 1; +} diff --git a/src/util/SFile.hpp b/src/util/SFile.hpp index 1e3db95..350859d 100644 --- a/src/util/SFile.hpp +++ b/src/util/SFile.hpp @@ -39,6 +39,8 @@ class SFile { static int32_t SetDataPath(const char* path); static int32_t GetBasePath(char* path, size_t capacity); static int32_t GetDataPath(char* path, size_t capacity); + static int32_t SetDataPathAlternate(const char* path); + static int32_t RebuildHash(); // Member variables SFILE_TYPE m_type; diff --git a/src/util/StringTo.cpp b/src/util/StringTo.cpp index f7b9f02..aaf1f77 100644 --- a/src/util/StringTo.cpp +++ b/src/util/StringTo.cpp @@ -150,7 +150,7 @@ int32_t StringToJustify(const char* string, uint32_t& justify) { { 0x20, "BOTTOM" } }; - for (int32_t i = 0; i < 5; i++) { + for (int32_t i = 0; i < 6; i++) { if (!SStrCmpI(justifyMap[i].string, string, 0x7FFFFFFFu)) { justify = justifyMap[i].value; return 1;