diff --git a/AI_PROJECT_ANALYSIS.md b/AI_PROJECT_ANALYSIS.md
new file mode 100644
index 0000000..f328302
--- /dev/null
+++ b/AI_PROJECT_ANALYSIS.md
@@ -0,0 +1,211 @@
+# xiloader - Comprehensive AI Reference Guide
+
+This document contains detailed technical analysis of the xiloader project for future AI reference.
+
+## Project Overview
+
+**xiloader** is a C++ boot loader for Final Fantasy XI that bypasses PlayOnline authentication to connect to private FFXI servers.
+
+- **Original Authors**: DarkStar Dev Teams (2010-2015)
+- **Current Maintainer**: LandSandBoat Dev Teams (2021-present)
+- **License**: GNU GPL v3
+- **Repository**: https://github.com/LandSandBoat/xiloader
+- **Purpose**: Launch FFXI client without PlayOnline to connect to private servers
+
+## Build System & Requirements
+
+### Critical Build Requirements
+- **ONLY 32-bit builds supported** - 64-bit will fail intentionally
+- Visual Studio 2022 C++ Redistributable required
+- CMake 3.15 minimum
+- C++17 standard
+- Windows only (uses Windows COM, registry, and DLL injection)
+
+### Build Process
+```bash
+mkdir build
+cmake -S . -B build -A Win32 # -A Win32 is MANDATORY
+cmake --build build
+```
+
+### Dependencies (via CPM)
+- **detours**: Microsoft Detours library for DLL hooking
+- **argparse**: Command-line argument parsing
+- **mbedtls**: SSL/TLS cryptographic library for secure connections
+- **System Libraries**: crypt32, psapi, ws2_32, iphlpapi
+
+## Architecture & Technical Details
+
+### Core Components
+
+1. **main.cpp**: Entry point, argument parsing, COM initialization, game launch
+2. **network.cpp/h**: Socket management, proxy servers, SSL connections
+3. **functions.cpp/h**: Pattern scanning, registry access, helper utilities
+4. **console.cpp/h**: Colored console output with timestamps
+5. **defines.h**: Constants, COM GUIDs, language enumerations
+
+### Network Architecture
+
+**Port Configuration (Defaults)**:
+- Auth Server: 54231
+- Login Data: 54230
+- Login View: 54001
+- Game Server: 51220
+
+**Network Flow**:
+1. Detours winsock functions (gethostbyname, send, recv, connect)
+2. Creates local proxy servers on configured ports
+3. Intercepts FFXI network traffic and redirects to private server
+4. Handles XIFF protocol commands for lobby communication
+5. Uses mbedtls for SSL/TLS authentication only (Windows cert store provides CA certificates)
+
+### Memory Patching & DLL Injection
+
+**Pattern Scanning Targets**:
+- `polcore.dll` / `polcoreeu.dll`: INET mutex and POL connection functions
+- `FFXiMain.dll`: Hairpin NAT fix locations
+
+**Key Functions Located**:
+- INET Mutex: Pattern `\x8B\x56\x2C\x8B\x46\x28\x8B\x4E\x24\x52\x50\x51`
+- POL Connection: Pattern `\x81\xC6\x38\x03\x00\x00\x83\xC4\x04\x81\xFE`
+
+### COM Integration
+
+**PlayOnline COM Objects**:
+- POLCoreCom: Different CLSIDs for JP/US/EU
+- FFXiEntry: Single CLSID for game launching
+
+**Language Support**:
+- Japanese (0): `polcore.dll`
+- English (1): `polcore.dll`
+- European (2): `polcoreeu.dll`
+
+## XIFF Protocol Commands
+
+The loader handles these lobby protocol commands:
+- `0x07`: Login to character with account/character ID
+- `0x14`: Character deletion request
+- `0x1F`: Character list request
+- `0x21`: Character creation notification (client-side)
+- `0x22`: Character creation request (server-side)
+- `0x24`: Server name request
+- `0x26`: Version information exchange
+- `0x28`: Character rename information (GM function)
+- `0x2B`: GM character world move
+
+## Command Line Usage
+
+### Primary Options
+```bash
+xiloader --server
--user --pass
+```
+
+### All Available Arguments
+- `--server`: Server address to connect to
+- `--user/--username`: Account username
+- `--pass/--password`: Account password
+- `--email`: Email (currently unused)
+- `--serverport`: Lobby server port (default: 51220)
+- `--dataport`: Login data port (default: 54230)
+- `--viewport`: Login view port (default: 54001)
+- `--authport`: Login auth port (default: 54231)
+- `--lang`: Language JP/US/EU or 0/1/2
+- `--hairpin`: Enable hairpin NAT fix for local servers
+- `--hide`: Hide console window after FFXI starts
+
+### Helper Tools
+
+**xi_checker.bat**:
+- Locates PlayOnline installation via registry
+- Verifies DirectPlay is installed
+- Launches xiloader with proper paths
+- Usage: `xi_checker.bat `
+
+## Development Configuration
+
+### Code Style
+- Uses clang-format with WebKit base + Allman braces
+- 4-space indentation, no tabs
+- Column limit disabled for flexibility
+- Namespace indentation enabled
+
+### Git Configuration
+**Ignored Files**:
+- Build directories: `Debug/`, `Release/`, `bin/`, `build/`
+- IDE files: `.vs/`, `.vscode/`, `*.user`
+- Compiled outputs: `*.exe`, `*.pdb`, `*.ilk`, `*.exe.manifest`
+
+## Security & SSL Implementation
+
+**Important**: SSL/TLS is **only used for authentication**, not for main game traffic.
+
+### Certificate Handling
+- Uses **Windows Certificate Store APIs** to extract root certificates
+- `CertOpenStore` and `CertEnumCertificatesInStore` extract system certificates
+- Certificates are **converted from Windows format to mbedtls format** using `mbedtls_x509_crt_parse`
+- mbedtls then uses these certificates for SSL verification during authentication only
+
+### SSL State Management (Auth Server Only)
+Global SSL state includes:
+- mbedtls_net_context server_fd
+- mbedtls_ssl_context ssl
+- mbedtls_ssl_config conf
+- mbedtls_x509_crt cacert
+- mbedtls_entropy_context entropy
+- mbedtls_ctr_drbg_context ctr_drbg
+
+### Network Security Architecture
+- **Auth server communication**: SSL/TLS encrypted using mbedtls
+- **Login server communication**: Plain TCP sockets
+- **Game server communication**: Plain TCP sockets
+- **Lobby proxy servers**: Plain TCP sockets
+
+## Hairpin NAT Fix
+
+For local server deployments that are publicly exposed:
+
+**Implementation**:
+1. Scans FFXiMain.dll for hairpin locations
+2. Creates code cave with `HairpinFixCave` function
+3. Patches memory with JMP instructions
+4. Redirects server address resolution
+
+**Pattern Targets**:
+- Main hairpin: `\x8B\x82\xFF\xFF\xFF\xFF\x89\x02\x8B\x0D`
+- Zone change: `\x8B\x0D\xFF\xFF\xFF\xFF\x89\x01\x8B\x46`
+
+## Registry Integration
+
+**PlayOnline Registry Keys**:
+- US: `HKEY_LOCAL_MACHINE\SOFTWARE\PlayOnlineUS`
+- EU: `HKEY_LOCAL_MACHINE\SOFTWARE\PlayOnlineEU`
+- JP: `HKEY_LOCAL_MACHINE\SOFTWARE\PlayOnlineJP`
+- Wow64: Same paths under `SOFTWARE\Wow6432Node\`
+
+**Registry Values**:
+- InstallFolder: PlayOnline installation directory
+- Language settings and regional configurations
+
+## Version Information
+
+Current version: 1.1.5 (must match server expectations)
+Version format: x.x.x with single digit components
+Update locations: globals in main.cpp and xiloader.rc.in
+
+## Development Notes
+
+### Critical Implementation Details
+1. Only 32-bit compilation supported - enforced at CMake level
+2. Requires specific VC2022 redistributable for end users
+3. Pattern scanning is version-dependent and may break with game updates
+4. COM object instantiation requires proper registry configuration
+5. Memory patching uses naked assembly functions for hairpin fix
+
+### Maintenance Considerations
+- Pattern signatures may need updates with FFXI client patches
+- Registry keys vary by PlayOnline region/language
+- SSL certificate handling requires Windows-specific APIs
+- Thread management for proxy servers needs careful cleanup
+
+---
+*Generated by AI analysis for xiloader project reference*
diff --git a/src/command_handler.h b/src/command_handler.h
index a17999f..e9eb32d 100644
--- a/src/command_handler.h
+++ b/src/command_handler.h
@@ -40,6 +40,7 @@ namespace globals
extern bool g_FirstLogin;
extern std::string g_TrustToken;
extern bool g_TrustThisComputer;
+ extern std::string g_LoginToken;
} // namespace globals
#include "defines.h"
@@ -161,6 +162,20 @@ bool handleLoginCommand(int8_t command, json& login_reply_json, uint32_t& accoun
return false;
}
+ // LOGIN_ERROR_LAUNCH_TOKEN_INVALID
+ case 0x0014:
+ {
+ // Launch tokens are single-use and short-lived; an invalid one means
+ // the launcher's session lapsed between minting and boot.
+ globals::g_LoginToken.clear();
+ xiloader::console::output(xiloader::color::error, "==========================================================");
+ xiloader::console::output(xiloader::color::error, "Launch token invalid or expired (session expired).");
+ xiloader::console::output(xiloader::color::error, "Please log in again from the launcher.");
+ xiloader::console::output(xiloader::color::error, "==========================================================");
+
+ return false;
+ }
+
// LOGIN_SUCCESS_CREATE_TOTP
case 0x0010:
{
diff --git a/src/main.cpp b/src/main.cpp
index ff30350..5aac2a6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -60,6 +60,7 @@ namespace globals
bool g_FirstLogin = false; // set to true when --user --pass are both set to allow for autologin
std::string g_TrustToken = ""; // trust token loaded from disk or received from server
bool g_TrustThisComputer = false; // user checkbox / CLI flag for "trust this computer"
+ std::string g_LoginToken = ""; // single-use launch token from the launcher; sent in place of password + OTP
char* g_CharacterList = NULL; // Pointer to the character list data being sent from the server.
bool g_IsRunning = false; // Flag to determine if the network threads should hault.
@@ -569,8 +570,14 @@ int __cdecl main(int argc, char* argv[])
globals::g_Username = maybeUsername.value_or(globals::g_Username);
globals::g_Password = maybePassword.value_or(globals::g_Password);
- // Set autologin if it isn't set already
- if (maybeUsername.has_value() && maybePassword.has_value())
+ // Launcher-injected single-use launch token. Sent in place of
+ // password + OTP (Discord-login boot); wins over any password.
+ globals::g_LoginToken = jsonGet(jsonData, "login_token").value_or(globals::g_LoginToken);
+
+ // Set autologin if it isn't set already. A launch token boot has a
+ // username + token but no password, so it also enables autologin.
+ if (maybeUsername.has_value() &&
+ (maybePassword.has_value() || !globals::g_LoginToken.empty()))
{
globals::g_FirstLogin = true;
}
@@ -598,11 +605,14 @@ int __cdecl main(int argc, char* argv[])
globals::g_OtpCode = jsonGet(jsonData, "otp").value_or(globals::g_OtpCode);
globals::g_Email = jsonGet(jsonData, "email").value_or(globals::g_Email);
+ // Launcher-injected trust token; sent in place of an OTP, wins over disk.
+ globals::g_TrustToken = jsonGet(jsonData, "trust_token").value_or(globals::g_TrustToken);
+
bUseHairpinFix = jsonGet(jsonData, "hairpin").value_or(bUseHairpinFix);
globals::g_Hide = jsonGet(jsonData, "hide").value_or(globals::g_Hide);
globals::g_TrustThisComputer = jsonGet(jsonData, "trust_this_computer").value_or(globals::g_TrustThisComputer);
- std::string language = jsonGet(jsonData, "language").value_or({});
+ std::string language = jsonGet(jsonData, "language").value_or(std::string{});
setLanguage(language);
}
diff --git a/src/network.cpp b/src/network.cpp
index 7b6c3df..8e8cba7 100644
--- a/src/network.cpp
+++ b/src/network.cpp
@@ -51,6 +51,7 @@ namespace globals
extern bool g_FirstLogin;
extern std::string g_TrustToken;
extern bool g_TrustThisComputer;
+ extern std::string g_LoginToken;
}
// mbed tls state
@@ -383,8 +384,11 @@ namespace xiloader
{
menus::enterCredentialsWithOTP(globals::g_Username, globals::g_Password, globals::g_OtpCode, &globals::g_TrustThisComputer, globals::g_ServerAddress);
- // Load trust token for this server+user combination
- globals::g_TrustToken = loadTrustToken(globals::g_ServerAddress, globals::g_Username);
+ // Load from disk unless a token was already injected (launcher JSON).
+ if (globals::g_TrustToken.empty())
+ {
+ globals::g_TrustToken = loadTrustToken(globals::g_ServerAddress, globals::g_Username);
+ }
command = 0x10; // login
break;
@@ -481,8 +485,11 @@ namespace xiloader
command = 0x10;
globals::g_FirstLogin = false;
- // Load trust token for autologin
- globals::g_TrustToken = loadTrustToken(globals::g_ServerAddress, globals::g_Username);
+ // Load from disk unless a token was already injected (launcher JSON).
+ if (globals::g_TrustToken.empty())
+ {
+ globals::g_TrustToken = loadTrustToken(globals::g_ServerAddress, globals::g_Username);
+ }
}
json login_json;
@@ -493,10 +500,17 @@ namespace xiloader
login_json["version"] = globals::g_VersionNumber;
login_json["command"] = command;
- if (command == 0x10) // Only send trust fields for login
+ if (command == 0x10) // Only send trust/launch fields for login
{
login_json["trust_token"] = globals::g_TrustToken;
login_json["trust_this_computer"] = globals::g_TrustThisComputer;
+
+ // Single-use launch token (Discord-login boot). When present, xi_connect
+ // consumes it in place of password + OTP; password may be empty.
+ if (!globals::g_LoginToken.empty())
+ {
+ login_json["login_token"] = globals::g_LoginToken;
+ }
}
std::string str = login_json.dump();
@@ -526,7 +540,7 @@ namespace xiloader
bool retval = false;
- std::string errorMessage = jsonGet(login_reply_json, "error_message").value_or({}); // {} = empty string
+ std::string errorMessage = jsonGet(login_reply_json, "error_message").value_or(std::string{}); // empty string
if (errorMessage.empty())
{