Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

This book explains the internal design of the Dark Ages 4.21 Stone client. It follows the program from process startup into its main loop, then separates the client into rendering, audio, input, user interface, file, event, and network systems.

Each subsystem is explained at two levels. The high-level section describes responsibilities, lifecycle, and data flow in approachable language. The code-level section follows function calls, decisions, addresses, arguments, and state. A function table closes each subsystem for readers who want a compact technical reference.

This is a living reverse engineering reference. Empty sections are intentional at the start of the project. They define where understood behavior will go without filling gaps with speculation.

Audience

The book is written primarily for people in the Dark Ages developer community. It assumes that many readers already understand the game, its terminology, and the broad shape of its clients and protocol. It does not assume that every reader is comfortable reading disassembly or following compiler-generated C++.

Ways to use this book

Understand a client system

Start with a subsystem’s high-level section to see its responsibilities, lifecycle, and connections to the rest of the client. Continue into the code-level section when you need the actual call path, arguments, decisions, and state.

Connect network behavior to client behavior

Protocol and networking pages connect message framing and fields to the client functions that parse, dispatch, and act on them. This path is intended for proxy and server developers who already know the wire side and want to understand what happens after a message reaches the client.

Investigate a quirk or version difference

Use subsystem headings and function tables to move from visible behavior to the responsible code. Where differences with another client version are established, the relevant page should state what changed and link the comparison to exact functions or data structures.

Implement a file format

Format pages provide field layouts, byte order, string rules, reader and writer behavior, validation, and parser quirks. They are intended to remain useful for formats that continue to appear in other clients and community tools.

Familiarity with C-like code is helpful for the technical sections. Platform-specific Windows, DirectX, compiler, and networking details are explained when they affect the client behavior being discussed.

Current state

Repository structure and documentation conventions are established. Subsystem behavior has not yet been mapped.

Scope and Method

Scope

The project covers behavior implemented by the analyzed Darkages.exe, including:

  • Process initialization, configuration, main loop, state changes, and shutdown
  • Rendering, surface management, palette behavior, sprites, text, and presentation
  • Sound effects, music, resource lifetime, and playback control
  • Keyboard, mouse, Win32 messages, and internal event delivery
  • File lookup, archive or container access, decoding, parsing, and serialization
  • Socket lifetime, connection states, packet framing, message dispatch, and field serialization
  • Byte transformations such as XOR when their exact behavior and order are understood
  • Compiler and runtime mechanisms when they materially explain application behavior

Compatibility wrappers such as cnc-ddraw are outside the client architecture unless their interaction reveals a client requirement worth documenting.

Clean-room policy

The repository contains original documentation, diagrams, tables, and limited explanatory pseudocode derived from analysis. It does not contain the executable, supporting DLLs, game assets, private runtime captures, an IDA database, or reconstructed source code.

Local copies may be used as analysis inputs. Proprietary materials must never be committed. The ignored .local/ directory may hold a private working IDA database.

Two levels of explanation

Every subsystem chapter has a high-level section and a code-level section.

The high-level section explains responsibilities, lifecycle, data flow, and interactions with other parts of the client. It should be understandable without following assembly or decompiler output.

The code-level section follows the real implementation. It records call trees, function addresses, prototypes, arguments, return values, branches, state changes, important data, and cleanup paths. The chapter ends with a function table for readers who need a compact technical reference.

What belongs in the book

The book contains behavior confirmed through direct code analysis or supported with high confidence when code patterns and observed behavior agree. Speculative leads remain in local notes or tentative IDA names until they are understood well enough to explain clearly.

The decompiler is a useful view of the program, but its types and reconstructed control flow are not automatically correct. Disassembly and call sites should be checked whenever signedness, ownership, object layout, calling convention, or optimizer behavior affects the explanation.

Address and naming conventions

A function reference uses the form Darkages.exe:0x00401234, current IDA name net_receive_packet. Unless a page says otherwise, addresses are virtual addresses from the analyzed image rather than file offsets or RVAs.

IDA names are descriptive research names. They are not presented as original source symbols. The function address remains the stable reference if a name improves later.

Legal Disclaimer

Ownership

Dark Ages and all original game software, code, artwork, music, sound, text, characters, names, logos, trademarks, and related materials are the property of their respective copyright and trademark owners. Nothing in this project transfers, limits, or challenges those rights.

The contributors claim authorship only over the original documentation, diagrams, research notes, and explanatory pseudocode created for this project. References to the game and its behavior are included only where needed to explain the research.

Independent project

This is an independent and unofficial research project. It is not affiliated with, sponsored by, approved by, or endorsed by the developers, publishers, operators, or rights holders of Dark Ages.

Purpose and fair use

This project is undertaken in good faith for noncommercial education, scholarship, preservation, commentary, and interoperability research. The maintainers believe that the limited analysis, factual descriptions, and necessary references presented here are consistent with fair-use principles under 17 U.S.C. Section 107.

Fair use is determined from the facts of each use. An educational or noncommercial purpose does not automatically make every use fair. The U.S. Copyright Office Fair Use Index provides general information and examples, but this project does not offer legal advice or guarantee that every possible use of its material will qualify as fair use.

What is not distributed

This repository does not distribute the game executable, supporting DLLs, game assets, music, artwork, private packet captures, IDA databases, license material, or reconstructed proprietary source code. Contributors must never force-add or commit such material. A local IDA database may remain in the ignored .local/ workspace described in the README.

The documentation is not a substitute for obtaining the game or any other copyrighted work through lawful means. Users are responsible for ensuring that their own research, implementations, and distribution choices comply with applicable law and any agreements that apply to them.

Rights-holder concerns

A rights holder who believes this repository contains material that should not be present may contact the repository maintainers through the GitHub project. Please identify the material and explain the concern so it can be reviewed promptly.

Client Overview

This chapter connects the major runtime systems and explains how control and data move through the client as a whole.

High-level architecture

This section will describe the top-level application state, major subsystem boundaries, screen or game states, shared services, thread ownership, and the order in which input, networking, game logic, audio, and rendering are processed.

The client architecture has not been mapped yet.

Code-level coordination

This section will begin at the application entry point and follow the functions that construct, connect, update, and destroy the major systems. It will record global state, top-level objects, dispatch points, main-loop decisions, and cross-subsystem calls.

No top-level call tree is documented yet.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
No functions documented yet

UI, Input, and Packet Flows

This page connects visible in-game UI behavior to the internal Event system and the client and server packet actions that drive it. The most important distinction is ownership. Some panes are persistent and are only shown or hidden. Some are allocated in response to a server packet. Bulletin and mail use a longer-lived session object that repeatedly replaces its active child dialog.

The packet action byte is the first byte of the decoded logical packet. Packet offsets in this page are relative to that action byte unless stated otherwise.

High-level operation

End-to-end architecture

flowchart LR
    win["Win32 mouse and key messages"] --> queue["EventMan input queue"]
    queue --> input["Pane Event types 0 through 8"]
    input --> local["Pane mouse and key handlers"]
    local --> transition["Create, show, hide, or remove pane"]
    local --> builder["Client packet builder"]
    builder --> send["net_c_queue_send"]
    send --> encode["Sequence, XOR, and TCP frame"]
    encode --> server["Game server"]

    server --> tcp["TCP frame receive and XOR decode"]
    tcp --> socket["Pane socket Event type 9"]
    socket --> map["MapPane and registered child handlers"]
    map --> parser["Server-action parser"]
    parser --> transition
    parser --> state["Persistent pane or dialog fields"]
    state --> transition

    timer["Pane timers"] --> delay["Spell delay controller"]
    delay --> builder

Win32 messages do not call game panes directly. win_main_window_proc queues input in EventMan. The event worker converts it to internal Events, and the dispatcher calls the relevant pane virtual handler. Client packet builders then submit logical packets to net_c_queue_send.

The reverse path begins when the Socket worker completes and decodes a frame. EventMan creates Event type 9 and traverses the registered pane hierarchy. MapPane owns most in-game server actions, while persistent child panes such as the equipment, skill, and spell panes claim the actions that update their own state.

Pane transition models

ModelExamplesWhat “show” meansWhat closes or replaces it
Persistent content selectionstatus, chat, equipment, skill inventory, spell inventoryHide the previous content pane through vslot +0x18, show the selected pane through +0x14, then store it at GameButtons +0x124.Selecting another content pane. Equipment can also hide itself when selected twice.
Persistent server-populated paneequipment, usersThe object survives between updates. A packet changes fields and invalidates controls; visibility is a separate operation.Equipment is hidden locally. The users pane survives after it is first created and can be repopulated.
Dynamic dialogoptions, paper, screen menu, pursuit message variants, skill and spell detail dialogsAllocate, construct controls, add to the Screen tree, and register in the Event tree.The dialog’s control or generic Dialog key path unregisters, removes, and deletes or defers deletion.
Session with replaceable childbulletin and mailAllocate one BulletinSession, then replace its current BoardList, ArticleList, Article, MailList, or Mail child as responses arrive.Session destruction removes the active child and clears the static session root.
Dynamic slot childrenindividual skill and spell slotsServer add actions allocate a slot pane and add it to both registries. Parent inventory visibility is unchanged.The matching server remove action unregisters, removes, deletes, and clears the indexed pointer.

Server actions that affect UI

Server actionUI targetCreation or visibility effectState effect and handler
SStatus 0x08persistent status and related game panesDoes not select or show the status pane.Updates already registered status fields through several pane-local handlers, including Darkages.exe:0x0043F420 and Darkages.exe:0x00441850.
SMessage 0x0A, subtype 8, 9, or 10generic message dialogAllocates and registers a dynamic message dialog. Subtype 10 selects the alternate constructor mode.A big-endian text length at packet +2 controls the copied message. Tabs are normalized to carriage returns before construction.
SSelfSaveOk 0x21registered OptionPaneAllocates a local message dialog containing “Saved.”ui_option_pane_handle_server_packet claims the action. It reads no payload fields.
SAddSpell 0x17spell inventory slotAllocates, adds, and registers one spell slot pane. It does not show the parent spell inventory.ui_spell_inventory_handle_server_packet calls the add path after removing any old pane in the same slot.
SRemoveSpell 0x18spell inventory slotUnregisters, removes, and deletes one slot pane.Clears the indexed slot pointer.
SEnterEditingMode 0x1Bpaper dialogAllocates the same Paper dialog class in mode 0.Parses editing-mode fields before building controls.
SAddSkill 0x2Cskill inventory slotAllocates, adds, and registers one skill slot pane. It does not show the parent skill inventory.ui_skill_inventory_handle_server_packet replaces the indexed slot.
SRemoveSkill 0x2Dskill inventory slotUnregisters, removes, and deletes one slot pane.Clears the indexed slot pointer.
SScreenMenu 0x2Ffull-screen menu paneAllocates a 0x104-byte pane, adds it below BackgroundPane, and registers it.ui_screen_menu_pane_ctor parses the packet and later menu responses can update the registered menu receiver.
SPursuitMenu 0x30message or question dialog familyAllocates one of seven dynamic variants according to packet +1.Subtype 2 constructs QuestionMessageDialog; subtype 6 constructs QuestionMessageFaceDialog. The other five concrete source class names are not yet established.
SBulletin 0x31bulletin and mail sessionCreates a session for an allowed unsolicited packet when no session exists, or replaces the active child in an existing session.Subtypes 1 through 5 select board list, article list, article, mail list, and mail. Subtype 6 is accepted without creating a child.
SShowPaper 0x35paper dialogAllocates a 0x560-byte Paper dialog in mode 1 and registers it below BackgroundPane.Mode 1 parses the server text and display flags before building controls.
SShowUsers 0x36persistent Users Dialog PanePopulates the existing pane and explicitly shows it.Parses list metadata and variable-length user records, enables row controls, starts a 100 ms pane timer, and invalidates the region.
SAddEquip 0x37persistent User Equip paneDoes not show the pane.Stores a big-endian item identifier and length-prefixed name in the selected equipment slot, then redraws.
SRemoveEquip 0x38persistent User Equip paneDoes not hide the pane.Clears the selected slot identifier and name, then redraws.
SSelfLook 0x39persistent User Equip pane and embedded legend/detailsDoes not show the pane.Replaces character strings and appearance/detail fields, updates the embedded legend parser, and redraws.
SCooldown 0x3Fskill or spell slotDoes not select the parent pane.The skill and spell inventory handlers route the action to their respective slot cooldown update paths.
SReconnect 0x4C, subtype 1exit-wait paneKeeps the existing pane and replaces its warning with the safe-to-exit state.Sends CQuit 0x0B, subtype 0, completing the orderly-exit exchange. This branch does not reconnect a socket.

No established server action directly selects the persistent A/S/D/F/G content pane. Server status, skill, spell, and equipment actions update those panes even while another content pane is visible.

Client actions that affect UI or packets

Input or control actionImmediate UI effectClient trafficServer-driven continuation
A or inventory buttonSelects equipment. If it is already current and visible, hides it. If it is current but hidden, shows it again.Reopening the hidden current equipment pane first sends CSelfLook 0x2D, one byte with no payload. Switching from another content pane sends no self-look request.SSelfLook 0x39 refreshes data but does not itself show the pane.
S or skill buttonSelects the persistent skill inventory and hides the previous content pane.None merely for selection.SAddSkill, SRemoveSkill, and SCooldown maintain its slot children.
D or spell buttonSelects the persistent spell inventory and hides the previous content pane.None merely for selection.SAddSpell, SRemoveSpell, and SCooldown maintain its slot children.
F or chat buttonSelects the persistent chat pane.None merely for selection.Chat controls emit their own message actions when text is submitted.
G or status buttonSelects the persistent status pane.None.SStatus updates fields independently of selection.
Q or options buttonAllocates and registers a new dynamic OptionPane below BackgroundPane.The open action itself sends no packet. Option controls can send CUserSetting 0x1B with one setting-code byte.SSelfSaveOk 0x21 creates the local “Saved.” message dialog while the pane is registered.
X while OptionPane owns the shortcutAllocates ui_exit_wait_pane over the options UI.Its constructor sends CQuit 0x0B, subtype 1.SReconnect 0x4C, subtype 1, sends the subtype 0 completion and changes the pane to safe-to-exit text.
Right down on a skill slotAllocates and registers SkillBookDialog.None.None required.
Activate a skill slotRuns the slot’s activation path.Optional CSpellDelaySay 0x4E, followed by CUseSkill 0x3E with one slot byte.Cooldown state can later arrive through action 0x3F.
Right up on a spell slotAllocates and registers SpellBookDialog.None.None required.
Activate a spell slotChooses an immediate, targeting, or text-input path from the spell’s type byte.The confirmed immediate path builds action 0x0F plus one slot byte. With a configured delay it first sends CSpellDelayRequest 0x4D and timed CSpellDelaySay 0x4E packets, then sends the held 0x0F packet.Target selection or input completion supplies any additional cast context before the packet enters the delay controller.
W or bulletin buttonAllocates and registers BulletinSession.Sends CBulletin 0x3B, subtype 1.SBulletin subtype 1 creates the first BoardList dialog.
Bulletin or mail navigation controlsKeeps the session and changes the active child when a response arrives.CBulletin subtype 2 requests lists, 3 requests an article or mail, 4 posts an article, 5 deletes, 6 posts mail, and 7 changes article highlight state.SBulletin subtypes 1 through 5 replace the active child.
E or users buttonLazily creates Users Dialog Pane, immediately hides it, and leaves it registered. Existing panes are reused.Sends one-byte CWho 0x18 on every activation.SShowUsers 0x36 parses the reply and shows the pane.
Screen-menu selection or text submissionUses the currently registered SScreenMenu pane.Sends CMenuCode 0x39. Every traced variant begins with a menu type byte, a big-endian object identifier, and a big-endian menu code, followed by variant-specific selection or text.Later SScreenMenu or SPursuitMenu traffic can replace or update the interaction.
Question dialog answerLeaves the response to the dialog’s answer virtual.QuestionMessageDialog and its face variant send CMessage 0x3A, including the dialog type, object and pursuit identifiers, response marker 1, and selected answer byte.Server pursuit traffic determines the next dialog.
Paper submit or editing completionNormalizes carriage returns to tab bytes for the packet and closes through the Paper path.Sends CExitEditingMode 0x23 with mode, a big-endian text length, and the text bytes.Server behavior after submission is outside the local UI transition.

Bulletin and mail child replacement

The session owns a ten-entry dialog history array beginning at session +0xF8, the current index at +0xF5, and the active child at +0x120. Each successful response constructs a new dialog, detaches or queues deletion of superseded children, stores the new child in the history, sets the child’s back-pointer to the session, and invokes the child’s class-specific build/register virtual.

SBulletin subtypeChild resultMatching client request family
1BoardListDialogCBulletin subtype 1, start session
2ArticleListDialogCBulletin subtype 2, list request
3ArticleDialogCBulletin subtype 3, article request
4MailListDialogCBulletin subtype 2, mail-list request
5MailDialogCBulletin subtype 3, mail request
6No new childCompletion or acknowledgement for a prior write operation

Article-list and mail-list responses are accepted only while the session’s request-wait flag at +0x124 is set. This prevents an unsolicited list response from replacing the active child.

Spell-delay sequence

The spell slot builds the final action before the delay begins. The delay controller copies that logical packet into its own buffer and records its length, so the final timer callback does not reconstruct the cast.

sequenceDiagram
    participant Input as Spell slot input
    participant Delay as Spell delay controller
    participant Queue as Client send queue
    participant Server as Server

    Input->>Delay: Hold packet 0x0F, slot
    alt delay is zero
        Delay->>Queue: Queue held 0x0F packet
    else delay is positive
        Delay->>Queue: CSpellDelayRequest 0x4D, seconds
        loop once per second
            Delay->>Queue: CSpellDelaySay 0x4E, configured phrase
            Delay->>Delay: Schedule next 1000 ms timer
        end
        Delay->>Queue: Queue held 0x0F packet
    end
    Queue->>Server: Sequence, XOR, and frame each logical packet

The phrases are loaded from SpellBook.cfg and matched against the current spell name. A runtime packet hook that only watches the initial mouse event will therefore miss the later timer-owned final send.

Runtime observation roots and useful fields

The Runtime UI Memory Map gives the complete read-only Screen and Event tree traversal, vtable discriminators, field layouts, and stable snapshot procedure.

The virtual addresses below assume image base 0x00400000. External tools should use loaded_module_base + RVA.

VARVACurrent IDA nameObject and useful chain
Darkages.exe:0x004E2D700x000E2D70ui_bulletin_sessionActive heap BulletinSession, or null. Child history begins at +0xF8, current index is +0xF5, active child is +0x120, request-wait flag is +0x124.
Darkages.exe:0x004E32A40x000E32A4ui_equip_panePersistent heap User Equip pane. Slot 1 through 13 identifiers are at +0xB22 + 2 * slot; names are 0x80-byte records at +0xB3E + 0x80 * (slot - 1); embedded legend/details ownership begins at +0x11CC.
Darkages.exe:0x004E35640x000E3564ui_users_dialog_paneHeap Users Dialog Pane, or null before first CWho. It is reused across SShowUsers replies.
GameButtons owner field +0x124dynamiccurrent content panePoints at the currently selected status, chat, equipment, skill, or spell content pane. Find the owner through the Screen or Event registry.
GameButtons owner fields +0x130 through +0x140dynamiccontent pane pointers+0x130 chat, +0x134 status, +0x138 equipment, +0x13C skill inventory, +0x140 spell inventory.

These pointers are observation roots, not synchronization primitives. Dialog removal may use deferred deletion, and the Screen and Event registries can change while another thread reads them. Validate the pane vtable and take a stable registry snapshot before following dynamic children. Writing a visible flag alone is unsafe because Screen composition, Event eligibility, capture, timers, and owner pointers must agree.

Code-level flow

Persistent content selection

ui_game_buttons_handle_key_event accepts Event type 8 and maps A/S/D/F/G to indices 0 through 4. ui_game_buttons_select_content dispatches to the relevant selector. A selector hides the previous pane through vslot +0x18, shows the new pane through +0x14, stores it at owner +0x124, updates the associated button layout, and redraws.

Equipment has an additional same-pane branch. ui_game_buttons_select_inventory calls ui_pane_is_visible. A visible current equipment pane is hidden. A hidden current pane causes net_c_send_self_look to queue action 0x2D, then the pane is shown. This order means the old equipment data can become visible before the asynchronous SSelfLook reply arrives.

Equipment and self-look

ui_equip_pane_ctor publishes ui_equip_pane, while its destructor clears the pointer. ui_equip_pane_handle_server_packet claims only actions 0x37, 0x38, and 0x39.

ui_equip_pane_add_item reads the slot at packet +1, a big-endian identifier at +2, the name length at +4, and name bytes at +5. ui_equip_pane_remove_item reads the slot at +1 and clears the corresponding fields. ui_equip_pane_apply_self_look replaces the larger appearance, identity, and legend state before invalidating the pane.

Skill and spell inventories

ui_skill_inventory_handle_server_packet dispatches 0x2C, 0x2D, and 0x3F. ui_spell_inventory_handle_server_packet dispatches 0x17, 0x18, and 0x3F. Both add handlers first remove an existing pane at the packet-selected index, then allocate a class-specific slot object and add it to both registries. Both remove handlers perform the inverse sequence and clear the slot pointer.

ui_skill_slot_handle_mouse_event constructs SkillBookDialog on right-button down inside the slot. The activation branch sends the optional action 0x4E phrase and then net_c_send_use_skill, which emits [0x3E, slot].

ui_spell_slot_handle_mouse_event constructs SpellBookDialog on right-button up inside the slot. Its activation dispatcher selects immediate cast, targeted cast, or an input dialog from the spell type at slot +0xF8. ui_spell_slot_begin_cast builds [0x0F, slot] and passes it to ui_spell_delay_begin. A nonzero configured delay sends action 0x4D, installs a 1000 ms callback, emits action 0x4E phrases, and releases the held cast in ui_spell_delay_handle_timer.

Bulletin and mail

ui_window_buttons_show_bulletin_session allocates 0x12C bytes and calls ui_bulletin_session_ctor in client-opened mode. The constructor publishes ui_bulletin_session, adds and registers the session below BackgroundPane, then calls net_c_send_bulletin_start.

An unsolicited SBulletin reaches ui_map_handle_bulletin. When no session exists and the packet flag allows creation, the map handler allocates the same object in server-opened mode and passes the original packet to the constructor. When a session already exists, its registered socket handler claims the packet directly.

ui_bulletin_session_process_packet reads packet +1 and dispatches to the five child processors. Each processor handles history cleanup, attaches the new child, sets the session back-pointer, and calls the child build virtual. Article and mail identifiers of zero produce a small “No More” dialog instead of an Article or Mail child.

Users, paper, and server menus

Window button Q calls ui_window_buttons_show_option_pane, which allocates 0x578 bytes and constructs OptionPane. ui_option_pane_ctor installs ui_option_pane_vtable, builds the controls, and adds and registers the pane below BackgroundPane. Its mouse, key, socket, and timer vslots are all class-specific. Option controls call net_c_send_user_setting; a registered OptionPane claims SSelfSaveOk and constructs the local confirmation dialog. Its X shortcut constructs the exit-wait pane, beginning the two-step CQuit and SReconnect orderly-exit exchange.

Window button E always calls net_c_send_who_request. On first use it also allocates Users Dialog Pane, publishes ui_users_dialog_pane, adds and registers it, and immediately calls vslot +0x18 to keep it hidden. ui_map_handle_show_users later obtains that static object, calls ui_users_dialog_parse_server_list, and then ui_users_dialog_show.

ui_map_handle_show_paper allocates Paper in mode 1. SEnterEditingMode uses the same constructor in mode 0. Both parsers eventually call ui_paper_dialog_build_content, which creates the controls, adds the dialog below BackgroundPane, and registers it.

ui_paper_dialog_handle_completion reads the text control, changes carriage returns to tab bytes, serializes CExitEditingMode, and closes the dialog when its completion conditions permit submission. net_c_send_exit_editing_mode uses the same packet layout without owning the close transition.

ui_map_handle_screen_menu allocates and constructs one full-screen menu pane for action 0x2F. Menu interaction functions serialize CMenuCode action 0x39. ui_map_handle_pursuit_menu reads packet +1 and allocates one of seven message classes. The two friendly question class names are proven by constructor diagnostics and vtable installation. Their answer virtuals serialize CMessage action 0x3A.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
Darkages.exe:0x0040EB10ui_bulletin_session_ctorvoid *__thiscall(void *, int, const uint8_t *)Construct and publish a bulletin/mail session.Called for client-opened W flow and allowed unsolicited SBulletin.
Darkages.exe:0x0040ECA4net_c_send_bulletin_startvoid __thiscall(void *)Send CBulletin subtype 1.Called by the client-opened session constructor path.
Darkages.exe:0x0040ED04ui_bulletin_session_process_packetvoid __thiscall(void *, const uint8_t *)Dispatch SBulletin subtype.Calls the five child-specific processors.
Darkages.exe:0x0040EE64ui_bulletin_session_process_board_listvoid __thiscall(void *, const uint8_t *)Construct and attach BoardListDialog.SBulletin subtype 1.
Darkages.exe:0x0040F8F4ui_bulletin_session_process_article_listvoid __thiscall(void *, const uint8_t *)Construct and attach ArticleListDialog.SBulletin subtype 2, gated by request-wait state.
Darkages.exe:0x00410A14ui_bulletin_session_process_articlevoid __thiscall(void *, const uint8_t *)Construct ArticleDialog or a no-more dialog.SBulletin subtype 3.
Darkages.exe:0x004118F4ui_bulletin_session_process_mail_listvoid __thiscall(void *, const uint8_t *)Construct and attach MailListDialog.SBulletin subtype 4, gated by request-wait state.
Darkages.exe:0x00412974ui_bulletin_session_process_mailvoid __thiscall(void *, const uint8_t *)Construct MailDialog or a no-more dialog.SBulletin subtype 5.
Darkages.exe:0x0042EAF0ui_equip_pane_handle_server_packetint __thiscall(void *, void *)Claim equipment and self-look packets.Calls add, remove, or full self-look parser.
Darkages.exe:0x0042EBA4ui_equip_pane_apply_self_lookvoid __thiscall(void *, const uint8_t *)Apply SSelfLook.Updates the persistent pane without changing visibility.
Darkages.exe:0x0043CF20ui_game_buttons_handle_key_eventint __thiscall(void *, void *)Map A/S/D/F/G to content panes.Calls ui_game_buttons_select_content.
Darkages.exe:0x0043D164ui_game_buttons_select_contentvoid __thiscall(void *, int8_t)Dispatch persistent pane selection.Indices 0 through 4 are inventory, skills, spells, chat, status.
Darkages.exe:0x0043D3D4ui_game_buttons_select_inventoryvoid __thiscall(void *)Select or toggle equipment.Hidden same-pane path sends CSelfLook.
Darkages.exe:0x0043D4C4net_c_send_self_lookvoid __cdecl(void)Send one-byte CSelfLook.Queues action 0x2D.
Darkages.exe:0x0043E1B4ui_window_buttons_selectvoid __thiscall(void *, int8_t)Dispatch Q/W/E window buttons.Bulletin is index 1; users is index 2.
Darkages.exe:0x0043E2B4ui_window_buttons_show_option_panevoid __thiscall(void *)Create a dynamic OptionPane.Q or window-button index 0.
Darkages.exe:0x0043E384net_c_send_who_requestvoid __cdecl(void)Send one-byte CWho.Users list request, action 0x18.
Darkages.exe:0x00442990ui_skill_inventory_handle_server_packetint __thiscall(void *, void *)Maintain skill slot panes.Handles 0x2C, 0x2D, and 0x3F.
Darkages.exe:0x00443B10ui_spell_inventory_handle_server_packetint __thiscall(void *, void *)Maintain spell slot panes.Handles 0x17, 0x18, and 0x3F.
Darkages.exe:0x00445830ui_users_dialog_parse_server_listvoid __thiscall(void *, const uint8_t *)Populate Users Dialog Pane.Called by ui_map_handle_show_users before showing.
Darkages.exe:0x00454C64net_c_send_use_skillvoid __thiscall(void *)Send [0x3E, slot].Called by skill activation.
Darkages.exe:0x00455684ui_spell_slot_begin_castvoid __thiscall(void *)Build [0x0F, slot].Passes the logical packet to the delay controller.
Darkages.exe:0x004556F4ui_spell_delay_beginestablished __thiscall methodHold or immediately send a spell packet.Loads SpellBook.cfg, sends delay traffic, and installs timer callback 0.
Darkages.exe:0x00455CA4net_c_send_spell_delay_requestvoid __thiscall(void *, uint8_t)Send action 0x4D.Payload is configured delay seconds.
Darkages.exe:0x00456940ui_spell_delay_handle_timerint __thiscall(void *, int, int, int)Advance delayed casting.Sends action 0x4E phrases and finally queues held 0x0F.
Darkages.exe:0x00469474ui_map_handle_screen_menuint __thiscall(void *, const uint8_t *)Create an SScreenMenu pane.Allocates and calls ui_screen_menu_pane_ctor.
Darkages.exe:0x0046C004ui_map_handle_pursuit_menuint __thiscall(void *, const uint8_t *)Create an SPursuitMenu dialog variant.Dispatches subtype at packet +1.
Darkages.exe:0x0046C3F4ui_map_handle_show_paperint __thiscall(void *, const uint8_t *)Create server-show Paper dialog.Uses constructor mode 1.
Darkages.exe:0x0046C464ui_map_handle_show_usersint __thiscall(void *, const uint8_t *)Populate and show Users Dialog Pane.Requires the pane created by the CWho input path.
Darkages.exe:0x0046CAB4ui_map_handle_bulletinint __thiscall(void *, const uint8_t *)Handle sessionless SBulletin.Creates server-opened session when allowed.
Darkages.exe:0x0047EFC0ui_question_message_dialog_ctorvoid *__thiscall(void *, const uint8_t *)Construct QuestionMessageDialog.SPursuitMenu subtype 2.
Darkages.exe:0x0047F9C0ui_question_message_dialog_send_answerestablished __thiscall methodSend selected question answer.Serializes 12 logical bytes beginning with CMessage 0x3A.
Darkages.exe:0x0047FC80ui_question_message_face_dialog_ctorvoid *__thiscall(void *, const uint8_t *)Construct faced question dialog.SPursuitMenu subtype 6.
Darkages.exe:0x0048F600ui_option_pane_ctorvoid *__thiscall(void *)Build and register OptionPane.Installs vtable 0x00520660.
Darkages.exe:0x004909E0ui_option_pane_handle_server_packetint __thiscall(void *, void *)Handle SSelfSaveOk.Creates the local “Saved.” message dialog.
Darkages.exe:0x00490A80ui_option_pane_handle_mouse_eventint __thiscall(void *, void *)Handle OptionPane mouse state.Delegates common behavior to Dialog.
Darkages.exe:0x00490D70ui_option_pane_handle_timerint __thiscall(void *, int, int, int)Advance OptionPane timer state.Handles callback 100 separately.
Darkages.exe:0x00490FE0ui_option_pane_handle_key_eventint __thiscall(void *, void *)Handle OptionPane keyboard shortcuts.Delegates unclaimed keys to Dialog.
Darkages.exe:0x00491AA4net_c_send_user_settingvoid __thiscall(void *, uint8_t)Send [0x1B, setting_code].Called by OptionPane-family controls.
Darkages.exe:0x004921F0ui_exit_wait_handle_server_packetint __thiscall(void *, void *)Complete orderly exit on action 0x4C, subtype 1.Sends CQuit subtype 0 and changes the pane text.
Darkages.exe:0x00492310ui_exit_wait_pane_ctorestablished __thiscall constructorCreate exit-wait pane.Sends CQuit subtype 1.
Darkages.exe:0x00492C10ui_pane_is_visibleint __thiscall(void *)Query pane visibility.Equipment same-pane toggle uses it.
Darkages.exe:0x00492C40ui_pane_showvoid __thiscall(void *)Set visible state and invalidate.Common vslot +0x14.
Darkages.exe:0x00492D50ui_pane_hidevoid __thiscall(void *)Clear visible state and invalidate.Common vslot +0x18.
Darkages.exe:0x004939C0ui_paper_dialog_ctorvoid *__thiscall(void *, const uint8_t *, int)Construct Paper in either packet mode.Reaches ui_paper_dialog_build_content.
Darkages.exe:0x00493B60ui_paper_dialog_handle_completionint __thiscall(void *, int, int)Submit current Paper content when allowed, then close.Queues CExitEditingMode through the common send queue.
Darkages.exe:0x00494100net_c_send_exit_editing_modevoid __thiscall(void *)Submit current Paper content.Uses the same action 0x23 payload without closing the dialog.
Darkages.exe:0x004A9BC0ui_spell_book_dialog_ctorestablished __thiscall constructorConstruct local spell details.Called on right-button up over a spell slot.
Darkages.exe:0x004AB1D0ui_skill_book_dialog_ctorestablished __thiscall constructorConstruct local skill details.Called on right-button down over a skill slot.

Startup and Shutdown

The client uses a conventional Win32 process entry and a blocking GetMessageA loop, but its recurring game and timer work does not run in that loop. WinMain owns process checks, the main window, subsystem construction, and orderly teardown. A separate internal event worker supplies periodic callbacks. See Internal Event Routing for that worker.

High-level lifecycle

Process entry and window class

Windows enters the executable through the Microsoft Visual C++ runtime stub. The stub initializes the heap, multithreading runtime, standard I/O, command-line arguments, environment, and C initializers. It derives the command tail and the initial show command from STARTUPINFOA, obtains the module handle, and calls app_win_main. It always passes a null previous_instance, so the window-class registration path is taken in normal execution.

The client registers the ANSI window class DAClass with these established properties:

PropertyValue
Window procedurewin_main_window_proc
Class style0
Extra class and window bytes0
IconResource identifier 126
CursorIDC_ARROW
Background brushBLACK_BRUSH
MenuNone

If the first RegisterClassA call fails, the code calls RegisterClassA a second time while forming a diagnostic assertion, sets app_error_code to 1, and returns 0 from app_win_main. The second call does not recover startup.

Preflight checks and single-instance enforcement

app_win_main performs the following checks in order before it creates the window:

  1. It examines three values under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices: the unnamed default value, " .EXE" with a leading space, and "UMGR32.EXE". The key is requested with KEY_ALL_ACCESS. For the first value whose returned data begins with a nonzero byte, the client displays a message box with type 0x44. Selecting IDNO deletes that value and stops startup. Any other response permits startup. Failure to open the key permits startup.
  2. It checks a process-name list and two files. A match displays a yes-or-no prompt. Selecting IDNO returns 0 and stops startup; the other response continues the scan.
  3. It calls FindWindowExA(NULL, NULL, "#32770", "Brothers Speeder"). Finding that top-level dialog stops startup without creating the client window.
  4. It creates the initially owned mutex Nexon.SingleInstance. ERROR_ALREADY_EXISTS causes an immediate _exit(0). A mutex creation failure with another error is not treated as fatal, so this check fails open.

The process scan dynamically resolves CreateToolhelp32Snapshot, Process32First, and Process32Next from Kernel32.DLL. It runs on Windows 9x, or on the NT platform when the major version is at least 5. Executable paths are compared case-insensitively by suffix against this exact list:

EntryCompared process suffix
0Patch.exe
1boserve.exe
2winhwak.exe
3grcframe.exe

The implementation passes a local PROCESSENTRY32A to Process32First without first assigning its required dwSize field. It also does not close the process snapshot handle. Failure to load the library, resolve an entry point, create the snapshot, or begin enumeration skips to the file checks and therefore permits startup unless a later check rejects it.

The file checks use fopen(..., "rb") on <system-directory>\grcframe.exe and <windows-directory>\KeyHook.dll. Each existing file produces its own yes-or-no prompt.

These checks are documented as observed control flow. The runtime strings used for several prompts do not establish a broader intent for the checks.

Window creation

After the checks pass, app_win_main creates a topmost Dark Ages window of class DAClass. The extended style is WS_EX_TOPMOST. The style value 0x90880000 combines WS_POPUP, WS_VISIBLE, WS_BORDER, and WS_SYSMENU. Both coordinates are CW_USEDEFAULT, and the requested outer size is 642 by 482 pixels. The client later creates a 640 by 480 screen pane for presentation.

The return from CreateWindowExA is stored in app_main_window but is not validated. The code still calls SetForegroundWindow, ShowWindow, UpdateWindow, and app_initialize if the handle is null.

Subsystem initialization

app_initialize runs only after the window creation attempt. Its established setup order is:

  1. Fill a 1024-byte global buffer with ASCII spaces.
  2. Read physical-memory status and request a process working-set range from half of physical memory to all physical memory. Return values are ignored.
  3. Remove the IME context from the main window with ImmAssociateContext(window, NULL).
  4. Set the current directory from the executable path parsed out of the raw command line.
  5. Set the process priority class to HIGH_PRIORITY_CLASS, create the client memory manager, and seed rand with time(NULL).
  6. Open Legend.dat, seo.dat, and khan.dat. All three must open for that archive set to be accepted. Otherwise the client tries the single fallback archive DarkAges.dat.
  7. Create the rendering and media singletons, initialize rendering support, and load Skill.tbl.
  8. Construct the remaining service objects, the sound manager, and configuration. The sound path opened here is .\Music\1.mp3.
  9. If English.nfo exists, construct the configuration from Darkages.cfg; otherwise use Legend.cfg.
  10. Create the deferred-deletion queue, the 640 by 480 8-bit screen pane using Legend.pal, the internal event dispatcher, and the separate event_manager_instance. Both workers are resumed during this phase.
  11. Register the screen pane with the dispatcher, establish the 640 by 480 screen rectangle, load Darkages.prf or Legend.prf according to configuration byte +0x8D, and create the first terminal pane.

Most constructors communicate failure through the process-wide app_error_code. app_initialize contains staged cleanup branches for partial construction. When it returns, app_win_main checks the error code again and returns 0 instead of entering the message loop if it is nonzero.

The missing-archive path is different. It deletes the two early rendering or media singletons, shows two error boxes, posts WM_QUIT, and calls _exit(0). The first box labels GetCommandLineA output as the current directory, even though it did not call GetCurrentDirectoryA. The direct _exit bypasses app_shutdown.

Command-line directory parsing

app_set_working_directory_from_command_line does not use the command_line argument supplied to app_win_main. It calls GetCommandLineA again and applies this parser:

  • If any double quote exists, copy from the second command-line byte through the byte before the last quote.
  • Otherwise, copy up to the first space.
  • If there is no space, copy the complete command line.
  • Find the last backslash in the copied path, terminate the string there, and pass the directory to SetCurrentDirectoryA.

The helper ignores the SetCurrentDirectoryA result. Its local path buffer is fixed at approximately 256 bytes and the copies have no length check. Because it uses the last quote anywhere in the raw command line and always starts the quoted case at byte 1, quoted arguments after the executable can confuse the result.

Message pump and activation

Immediately before the first GetMessageA, app_win_main marks the window active and calls ui_screen_pane_activate if the screen pane exists. The message loop is blocking and unfiltered. It does not render or advance timers directly:

for (;;) {
    int get_message_result;

    get_message_result = GetMessageA(&message, NULL, 0, 0);
    if (get_message_result == 0) {
        break;
    }

    if (message.message == WM_QUIT || app_shutdown_requested == 1) {
        app_shutdown_requested = 1;
        break;
    }

    TranslateMessage(&message);
    DispatchMessageA(&message);
}

The explicit WM_QUIT comparison is normally redundant because GetMessageA returns zero for WM_QUIT. The code tests only for zero, so a GetMessageA error return of -1 is treated as a message and passed to TranslateMessage and DispatchMessageA. The shutdown flag is examined only after GetMessageA returns, so changing the flag alone does not wake a blocked loop.

win_main_window_proc connects the pump to client behavior. Relevant lifecycle cases include:

  • WM_DESTROY calls PostQuitMessage(0) and returns 0.
  • An inactive WM_ACTIVATEAPP path minimizes the window, beeps, and clears app_window_active.
  • The active WM_ACTIVATEAPP path repeats the Brothers Speeder window check. A match sets the shutdown flag and posts quit or close messages. Otherwise it restores the window, sets the active flag, and calls ui_screen_pane_activate.
  • WM_USER + 2 routes asynchronous Winsock notifications, as described in Networking.
  • Keyboard, mouse, palette, and client-specific messages are dispatched in the same procedure. The complete message table and both application-defined messages are documented in Input and Windows Events. Unknown messages go to DefWindowProcA.

Periodic timer processing is independent of this pump. The event worker waits with a 1 millisecond timeout and invokes the dispatcher tick. See Worker and timer lifecycle.

Normal and abnormal shutdown

Normal shutdown begins when GetMessageA returns zero or the loop observes app_shutdown_requested. app_win_main waits for a fixed 200 milliseconds, calls app_shutdown, closes and clears app_single_instance_mutex, and returns message.wParam. The CRT entry then passes that result to _exit.

For a normal WM_QUIT, message.wParam is the quit code. If the shutdown flag stops the loop while a different message is current, the function returns that message’s wParam instead.

app_shutdown is an ordered, null-safe deletion sweep. It closes the archive managers first, then destroys the constructed service singletons and global objects. Confirmed objects in the sweep include media and audio support, screen and UI state, configuration and preferences, the event dispatcher, the deferred-deletion queue, network-owned state, and the memory manager. Each owned global is set to null after deletion.

Deleting the event dispatcher releases its pane and timer containers, calls timeEndPeriod, and then invokes the worker base destructor. That base destructor uses TerminateThread, closes the worker and wait handles, and destroys queued synchronization state. It does not request a cooperative worker exit or join the thread. The full timing teardown is documented in Internal Event Routing.

Several exits do not run this sweep:

Exit pathBehavior
Window-class or preflight failureReturn 0 from app_win_main; no subsystem graph was constructed.
Existing Nexon.SingleInstance mutexCall _exit(0) immediately.
Missing required data archivesPerform limited early cleanup, display errors, post WM_QUIT, then call _exit(0).
Nonzero initialization errorUse staged cleanup inside app_initialize, then return 0 from app_win_main without calling the full app_shutdown.
CRT exception pathFilter the exception and call the CRT exit path with the exception code.

Code-level flow

CRT handoff

  1. Darkages.exe:0x004C62A5 app_crt_entry installs the runtime exception frame and initializes the heap, thread runtime, standard I/O, arguments, environment, and C initializers. Heap initialization failure exits with code 0x1C; multithreading initialization failure exits with code 0x10.
  2. It obtains the command tail from the CRT helper, derives show_command from STARTUPINFOA when STARTF_USESHOWWINDOW is present or uses SW_SHOWDEFAULT, and calls app_win_main(GetModuleHandleA(NULL), NULL, command_tail, show_command).
  3. A normal return calls _exit(win_main_result). The exception branch calls __XcptFilter and then the CRT exception exit routine.

app_win_main

  1. Clear app_main_window.
  2. When previous_instance == NULL, assemble and register DAClass. A registration failure sets app_error_code = 1 and returns 0.
  3. Save instance in app_instance.
  4. Call app_check_runservices_entries. Return 0 from app_win_main if it returns 1.
  5. Call app_check_conflicting_software. Return 0 if it returns 0.
  6. Call win_top_level_window_exists("#32770", "Brothers Speeder"). Return 0 on a match.
  7. Create Nexon.SingleInstance. Call _exit(0) when GetLastError() is ERROR_ALREADY_EXISTS.
  8. Create and show the main window, set app_window_active = 1, and call app_initialize.
  9. If app_error_code != 0, emit the diagnostic and return 0.
  10. Activate the screen pane and enter the GetMessageA loop.
  11. On loop exit, wait 200 milliseconds, call app_shutdown, close the mutex, and return the current MSG.wParam.

The command_line parameter is not read by this function. Directory setup re-reads the complete process command line.

Initialization and failure ownership

Darkages.exe:0x0045CCA0 app_initialize has no meaningful return value. Construction success is represented by nonnull globals and app_error_code == 0. Each major allocation is followed by an error check. Later failure labels delete the objects that were already created, proceeding backward through the partial dependency graph.

The successful path creates event_dispatcher with event_dispatcher_ctor, calls util_thread_queue_start to resume its suspended worker, then constructs event_manager_instance with event_manager_ctor and starts that worker. It registers ui_screen_pane with the dispatcher only after both exist.

The archive failure branch at Darkages.exe:0x0045CE6C does not join the common staged cleanup. It destroys the two early graphics or media objects, displays the archive errors, posts a quit message, and reaches _exit(0) at Darkages.exe:0x0045CF31.

Window messages and loop termination

Darkages.exe:0x0045BDC0 win_main_window_proc is the only window procedure installed for DAClass. DispatchMessageA reaches it for queued messages, while some client paths also post messages directly to app_main_window. Its WM_DESTROY case supplies the normal WM_QUIT. Other fatal paths set app_shutdown_requested and post a message so the blocking loop can observe the flag.

Darkages.exe:0x0049D790 ui_screen_pane_activate sets a screen-pane active field at +0x568, resets a presentation counter to 10, restores the pane’s render object through a virtual call, and invokes two screen update helpers. It is called once before the first GetMessageA and again after an active WM_ACTIVATEAPP transition.

Teardown

Darkages.exe:0x0045B8F0 app_shutdown checks each singleton or global before calling its deleting destructor. It then clears the corresponding global. The event dispatcher is deleted after the main screen, configuration, preference, and several UI service objects, but before the deferred-deletion queue and final media and memory singletons. No return code is propagated from teardown.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
Darkages.exe:0x0041D390app_check_conflicting_softwareint __cdecl(void)Prompt for four process suffixes and two files.Called by app_win_main; 0 stops startup. Toolhelp failures are fail-open. The local PROCESSENTRY32A.dwSize is not initialized and the snapshot is not closed.
Darkages.exe:0x0041D690win_top_level_window_existsBOOL __cdecl(LPCSTR class_name, LPCSTR window_title)Test for a matching top-level window.Thin FindWindowExA(NULL, NULL, ...) wrapper. Used during initial startup and active-window restoration.
Darkages.exe:0x0041D6C0app_check_runservices_entriesint __cdecl(void)Examine and optionally delete three RunServices values.Called first by app_win_main; 1 means a value was deleted after IDNO and startup must stop.
Darkages.exe:0x0045B5F0app_win_mainint __stdcall(HINSTANCE instance, HINSTANCE previous_instance, LPSTR command_line, int show_command)Own the Win32 lifecycle.Called only by app_crt_entry; calls the checks, creates the mutex and window, initializes subsystems, pumps messages, and shuts down.
Darkages.exe:0x0045B8F0app_shutdownvoid __cdecl(void)Delete the initialized subsystem graph in fixed order.Called only on the normal message-loop exit path. Null-checks and clears owned globals.
Darkages.exe:0x0045BDC0win_main_window_procLRESULT __stdcall(HWND window, UINT message, WPARAM wparam, LPARAM lparam)Dispatch all DAClass window messages.Registered by app_win_main; WM_DESTROY posts quit and WM_ACTIVATEAPP coordinates active state and screen restoration. See Input and Windows Events.
Darkages.exe:0x0045C840app_set_working_directory_from_command_linevoid __cdecl(void)Set the current directory to the parsed executable directory.Called near the start of app_initialize; re-reads the raw command line and ignores API failure.
Darkages.exe:0x0045CCA0app_initializevoid __cdecl(void)Construct resources, rendering, audio, configuration, event workers, screen state, and the initial pane.Called after window creation. Uses app_error_code and staged cleanup; the missing-archive branch calls _exit(0).
Darkages.exe:0x0049D790ui_screen_pane_activatevoid __thiscall(void *screen_pane)Restore active screen and presentation state.Called before the message loop and by the active WM_ACTIVATEAPP path.
Darkages.exe:0x004C62A5app_crt_entryvoid __cdecl(void)Initialize the Microsoft C runtime and hand off to app_win_main.PE process entry; passes the result to _exit and contains the CRT exception path.

Rendering

The rendering subsystem covers graphics initialization, drawing primitives, surface ownership, sprite composition, palette and color handling, text, frame assembly, and final presentation.

High-level operation

This section will explain how a frame moves from game and user interface state to the displayed image. It will be divided into graphics initialization, resource and surface management, scene composition, user interface drawing, presentation, lost-surface recovery, and shutdown.

Rendering behavior has not been mapped yet.

Code-level flow

This section will trace the graphics entry points and their call trees. It will record DirectDraw interfaces, object ownership, function arguments, coordinate systems, clipping decisions, layer order, palette behavior, frame timing, presentation calls, and error recovery.

No rendering call tree is documented yet.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
No functions documented yet

Audio

The audio subsystem covers sound initialization, music, sound effects, resource loading, playback, volume control, and shutdown.

High-level operation

This section will explain how audio resources enter the client, how music and sound effects are selected, what owns active playback, and which game or user interface events control sound.

Audio behavior has not been mapped yet.

Code-level flow

This section will trace audio initialization and playback call trees. It will record the Windows or DirectX interfaces in use, function arguments, resource lookup, caching, channel or buffer selection, volume calculations, stop behavior, failure handling, and cleanup.

No audio call tree is documented yet.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
No functions documented yet

Input and Windows Events

The DAClass window procedure is the boundary between the Win32 message queue and the client’s asynchronous event system. It handles a fixed set of 15 message values. Keyboard and mouse messages update immediate window or screen state, then enter the EventMan worker queue. Activation, palette, socket, virus-check, and destruction messages remain on the window thread.

High-level operation

Message-pump boundary

app_win_main blocks in GetMessageA, calls TranslateMessage, and sends each retrieved message through DispatchMessageA. The registered DAClass procedure is win_main_window_proc. This is an event-driven pump, not the rendering or game-tick loop. Timer work runs on the event-dispatcher worker described in Internal Event Routing.

The window procedure returns 0 for every message it recognizes. All other messages go to DefWindowProcA. This includes WM_CLOSE, so the default procedure destroys the window and the resulting WM_DESTROY case posts WM_QUIT. It also includes WM_CHAR, WM_PAINT, WM_SETFOCUS, WM_KILLFOCUS, WM_TIMER, and WM_QUERYNEWPALETTE. TranslateMessage can therefore produce a WM_CHAR, but the client has no explicit text-message branch here.

Complete handled-message table

All handler locations below are inside Darkages.exe:0x0045BDC0 win_main_window_proc.

ValueWin32 nameHandler addressPayload usedClient action
0x0002WM_DESTROYDarkages.exe:0x0045BE77NoneCall PostQuitMessage(0).
0x001CWM_ACTIVATEAPPDarkages.exe:0x0045C2F8wParam is the process activation state.Minimize and deactivate, or check for conflicting software and restore presentation state.
0x0100WM_KEYDOWNDarkages.exe:0x0045C21FScan and extended-key fields from lParam.Queue physical-key work code 0x0A.
0x0101WM_KEYUPDarkages.exe:0x0045C17AScan and extended-key fields from lParam.Queue key-release work code 0x0B.
0x0104WM_SYSKEYDOWNDarkages.exe:0x0045C295wParam is checked only for Alt+F4; the queued key comes from lParam.Apply the conditional Alt+F4 remap, then use the key-down path.
0x0105WM_SYSKEYUPDarkages.exe:0x0045C1F0Same interpretation as WM_SYSKEYDOWN.Apply the conditional Alt+F4 remap, then use the key-up path.
0x0200WM_MOUSEMOVEDarkages.exe:0x0045C112Low and high words of lParam.Update the software cursor and queue work code 4.
0x0201WM_LBUTTONDOWNDarkages.exe:0x0045C0C4Coordinates from lParam; wParam modifier bits are ignored.Update the cursor, set left-button state, and queue work code 5.
0x0202WM_LBUTTONUPDarkages.exe:0x0045C07DCoordinates from lParam; wParam modifier bits are ignored.Update the cursor, clear left-button state, and queue work code 6.
0x0204WM_RBUTTONDOWNDarkages.exe:0x0045C02ACoordinates from lParam; wParam modifier bits are ignored.Update the cursor, set right-button state, and queue work code 7.
0x0205WM_RBUTTONUPDarkages.exe:0x0045BF9ECoordinates from lParam; wParam modifier bits are ignored.Update the cursor, clear right-button state, and queue work code 8.
0x020AWM_MOUSEWHEELDarkages.exe:0x0045BFE8Signed high word of wParam is the wheel delta.Update the cursor and queue work code 9.
0x0311WM_PALETTECHANGEDDarkages.exe:0x0045C62EwParam is the window that changed the palette.Reload and apply legend.pal when another window changed the palette.
0x0402WM_USER + 2Darkages.exe:0x0045C2C4Low word of lParam is a Winsock network event.Queue socket receive work for FD_READ, or disconnect for FD_CLOSE.
0x2446WM_USER + 0x2046Darkages.exe:0x0045C412Nonzero wParam points to a virus-check module-name string; zero selects the scan-failure text. lParam is ignored.Route a virus-check warning to an active UI receiver or a modal LOD message box.

The last two rows are the only application-defined messages recognized by this window procedure.

Mouse input and message time

Every handled mouse message first performs the same software-cursor update under the critical section at screen-pane offset +0x534. The procedure restores the old cursor area with sub_49D850, calls ui_screen_pane_set_cursor_position, then draws or presents the new cursor area through the entry at sub_49D120. This occurs before EventMan input blocking is checked.

Coordinate handling has several exact quirks:

  • The low word of lParam is zero-extended. Values at least 640 become 639. The apparent negative clamp cannot be reached after zero extension.
  • The high word is obtained with a logical shift and is neither sign-extended nor clamped.
  • WM_MOUSEWHEEL goes through the same coordinate path. Windows supplies wheel coordinates in screen space, but this procedure does not call ScreenToClient before treating them like the other mouse coordinates.

Each message then calls GetMessageTime and places that 32-bit timestamp in the EventMan work record. Mouse movement allocates and transfers ownership of an eight-byte pair stored internally as (y, x), matching the screen-pane cursor calls. Button messages use the most recently stored coordinates. The worker divides a signed wheel delta by 120 before dispatching its mouse Event.

EventMan field +0x4B0 bit 0 blocks movement, wheel, button-down, and key-down queueing. Releases are deliberately different: left-button up, right-button up, and key up still queue while input is blocked. Button state is held at +0x4B4, with bit 0 for left and bit 1 for right.

The worker recognizes double-clicks itself rather than relying on WM_LBUTTONDBLCLK or WM_RBUTTONDBLCLK, neither of which is handled. It compares the new down timestamp with its stored timestamp and requires total horizontal plus vertical movement of at most two pixels. It dispatches mouse subtypes 1 and 2 for left single and double down, 3 for left up, 4 and 5 for right single and double down, 6 for right up, and 7 for wheel. Mouse movement uses subtype 0.

Keyboard input

The client queues physical scan codes rather than the wParam virtual-key value. Bits 16 through 23 of lParam supply the scan byte. If KF_EXTENDED bit 24 is set, the procedure adds 0x80 and keeps the low byte. A resulting zero is ignored. Repeat count and previous-key-state fields do not suppress repeated key-down messages.

uint8_t input_scan_code(uint32_t key_data)
{
    uint8_t scan_code = (uint8_t)(key_data >> 16);

    if ((key_data & 0x01000000U) != 0) {
        scan_code = (uint8_t)(scan_code + 0x80U);
    }

    return scan_code;
}

WM_SYSKEYDOWN and WM_SYSKEYUP contain one special path. If wParam is VK_F4, the Alt-context bit 29 is set, and Darkages.exe:0x0043DE20 returns a nonnull UI object, the code rewrites the scan byte to 0x10, clears the extended and Alt-context bits, and preserves selected high transition bits. The down and up messages then continue through their ordinary paths. The code establishes the remap but does not establish its original design intent.

On the EventMan worker, event_dispatch_key_down marks the scan code pressed, applies the client’s scan-code mapping and modifier tables, and dispatches key subtype 8 when the mapped value is nonzero. event_update_key_up clears pressed and modifier state but does not dispatch a key-up Event to panes.

Activation, palette, and shutdown messages

The application-level activation message is WM_ACTIVATEAPP, not WM_ACTIVATE. A zero wParam invokes a render-object virtual method, minimizes the window with SW_MINIMIZE, emits a 2000 Hz beep for 100 ms, and clears app_window_active. A nonzero wParam repeats the Brothers Speeder top-level-window check. A match sets app_shutdown_requested, posts quit, and posts WM_CLOSE. Otherwise the client restores a 640 by 480, 8-bit presentation configuration, uses SW_RESTORE, sets app_window_active, and calls ui_screen_pane_activate.

WM_PALETTECHANGED is ignored when the sender in wParam is the main window or when no screen pane exists. Otherwise render_screen_pane_load_palette reads 256 RGB entries from legend.pal, preserves the system-reserved palette entries at both ends, applies the palette, and marks the screen palette dirty. The procedure then calls the small screen-pane apply entry at Darkages.exe:0x0049D0E0.

WM_DESTROY only posts WM_QUIT. It does not perform subsystem cleanup itself. The message loop returns to app_win_main, which calls app_shutdown as described in Startup and Shutdown.

Application-defined messages

WM_USER + 2 is registered by net_connect_configured_server through WSAAsyncSelect with FD_READ | FD_CLOSE. win_main_window_proc uses only LOWORD(lParam): FD_READ calls net_queue_receive, and FD_CLOSE calls net_handle_disconnect. Other network-event values are ignored. The procedure also ignores the socket in wParam and the WSAGETSELECTERROR value in the high word of lParam. The receive and framing path is documented in Networking.

WM_USER + 0x2046 belongs to the process/module virus-check worker. app_virus_check_module_callback copies the first reported module name into the 256-byte app_virus_report_module buffer and posts the message with that pointer in wParam. The window procedure formats it as a quoted name. A zero wParam instead inserts app_virus_check_failure_detail into a Korean format string whose meaning is, “Could not perform the virus scan. ( %s ) Would you like to continue?”

The report is sent to the first available target in this order: the object returned by sub_4AD130, dword_4F51AC, then receiver. The procedure allocates a 0x554-byte report object and initializes it with sub_498CE0. If no target exists, it calls MessageBoxA with caption LOD and type MB_OK. The subsequent IDCANCEL shutdown test is not reachable through an ordinary MB_OK message box, although the code contains it.

Code-level flow

Dispatch and default processing

app_win_main
  -> GetMessageA
  -> TranslateMessage
  -> DispatchMessageA
      -> win_main_window_proc
          -> explicit handler for one of 15 message values
          -> DefWindowProcA for every other value

Darkages.exe:0x0045BDC0 win_main_window_proc is a direct comparison chain rather than a jump table. Its four parameters have the normal WNDPROC contract. Every explicit branch returns zero.

Mouse queue and worker path

win_main_window_proc
  -> sub_49D850
  -> ui_screen_pane_set_cursor_position
  -> sub_49D120
  -> event_manager_get_instance
  -> GetMessageTime
  -> event_queue_mouse_*                  work code 4 through 9
      -> util_thread_queue posting path
          -> event_process_work_item
              -> event_dispatch_mouse_*   Event subtype 0 through 7
                  -> event dispatcher pane routing
Windows inputQueue functionWork codeWorker handlerResult
Moveevent_queue_mouse_move4event_dispatch_mouse_moveMouse subtype 0 with copied coordinates.
Left downevent_queue_left_button_down5event_dispatch_left_button_downMouse subtype 1 or double-click subtype 2.
Left upevent_queue_left_button_up6event_dispatch_left_button_upMouse subtype 3.
Right downevent_queue_right_button_down7event_dispatch_right_button_downMouse subtype 4 or double-click subtype 5.
Right upevent_queue_right_button_up8event_dispatch_right_button_upMouse subtype 6.
Wheelevent_queue_mouse_wheel9event_dispatch_mouse_wheelMouse subtype 7 with wheel_delta / 120.

Keyboard queue and worker path

WM_KEYDOWN or WM_SYSKEYDOWN
  -> scan-code normalization
  -> event_queue_key_down                 work code 0x0A
      -> event_process_work_item
          -> event_dispatch_key_down      key subtype 8 when mapping succeeds

WM_KEYUP or WM_SYSKEYUP
  -> scan-code normalization
  -> event_queue_key_up                   work code 0x0B
      -> event_process_work_item
          -> event_update_key_up          state update, no pane Event

event_dispatch_key_down stores pressed state in the per-scan-code table beginning at EventMan offset +0x390. It combines the active modifier byte at +0x490 with mapping tables at +0x90, +0x190, and +0x290. One internal mapped value, 0x88 with state bit 0 set, also sets app_shutdown_requested and posts WM_CLOSE before the key Event is dispatched.

Socket and virus-check messages

net_connect_configured_server
  -> WSAAsyncSelect(..., WM_USER + 2, FD_READ | FD_CLOSE)
      -> win_main_window_proc
          -> net_queue_receive
          -> net_handle_disconnect

app_virus_check_thread
  -> module scanner callback
      -> app_virus_check_module_callback
          -> PostMessageA(..., WM_USER + 0x2046,
                          app_virus_report_module, 0)
              -> win_main_window_proc
                  -> active UI report receiver
                  -> MessageBoxA fallback

The module callback posts only the first infected-module name because it first requires app_virus_report_module[0] == 0. It also clears the virus-check continuation flag after posting, which causes the worker’s loaded scanner callbacks to stop their traversal.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
Darkages.exe:0x00432BD0event_queue_mouse_movevoid __thiscall(void *event_manager_object, int mouse_y, int mouse_x, unsigned int message_time)Copy coordinates and queue mouse-move work.Called only by win_main_window_proc; work code 4; blocked by +0x4B0 bit 0.
Darkages.exe:0x00432C50event_queue_left_button_downvoid __thiscall(void *event_manager_object, unsigned int message_time)Set left-button state and queue button-down work.Work code 5; blocked by +0x4B0 bit 0.
Darkages.exe:0x00432C90event_queue_left_button_upvoid __thiscall(void *event_manager_object, unsigned int message_time)Clear left-button state and queue release work.Work code 6; not suppressed by input blocking.
Darkages.exe:0x00432CC0event_queue_right_button_downvoid __thiscall(void *event_manager_object, unsigned int message_time)Set right-button state and queue button-down work.Work code 7; blocked by +0x4B0 bit 0.
Darkages.exe:0x00432D00event_queue_right_button_upvoid __thiscall(void *event_manager_object, unsigned int message_time)Clear right-button state and queue release work.Work code 8; not suppressed by input blocking.
Darkages.exe:0x00432D30event_queue_mouse_wheelvoid __thiscall(void *event_manager_object, int wheel_delta, unsigned int message_time)Queue the signed wheel delta and message time.Work code 9; blocked by +0x4B0 bit 0.
Darkages.exe:0x00432D60event_queue_key_downvoid __thiscall(void *event_manager_object, unsigned char scan_code, unsigned int message_time)Queue physical-key-down work.Work code 0x0A; blocked by +0x4B0 bit 0.
Darkages.exe:0x00432D90event_queue_key_upvoid __thiscall(void *event_manager_object, unsigned char scan_code, unsigned int message_time)Queue physical-key-release work.Work code 0x0B; not suppressed by input blocking.
Darkages.exe:0x00433060event_manager_get_instancevoid *__cdecl(void)Return the EventMan singleton.Asserts that event_manager_instance is nonnull; used for every queued input message.
Darkages.exe:0x00433434event_dispatch_mouse_movevoid __thiscall(void *event_manager_object, int mouse_y, int mouse_x, unsigned int message_time)Dispatch mouse subtype 0.Called by event_process_work_item for work code 4.
Darkages.exe:0x00433504event_dispatch_left_button_downvoid __thiscall(void *event_manager_object, unsigned int message_time)Dispatch left single- or double-button-down Event.Called for work code 5; subtypes 1 and 2.
Darkages.exe:0x004336C4event_dispatch_left_button_upvoid __thiscall(void *event_manager_object, unsigned int message_time)Dispatch left-button release.Called for work code 6; subtype 3.
Darkages.exe:0x00433794event_dispatch_right_button_downvoid __thiscall(void *event_manager_object, unsigned int message_time)Dispatch right single- or double-button-down Event.Called for work code 7; subtypes 4 and 5.
Darkages.exe:0x00433944event_dispatch_right_button_upvoid __thiscall(void *event_manager_object, unsigned int message_time)Dispatch right-button release.Called for work code 8; subtype 6.
Darkages.exe:0x00433A14event_dispatch_mouse_wheelvoid __thiscall(void *event_manager_object, int wheel_delta, unsigned int message_time)Normalize and dispatch wheel movement.Called for work code 9; divides delta by 120 and emits subtype 7.
Darkages.exe:0x00433AB4event_dispatch_key_downvoid __thiscall(void *event_manager_object, unsigned char scan_code, unsigned int message_time)Update key state and dispatch a mapped key Event.Called for work code 0x0A; can also request WM_CLOSE.
Darkages.exe:0x00433C34event_update_key_upvoid __thiscall(void *event_manager_object, unsigned char scan_code, unsigned int message_time)Clear pressed and modifier state.Called for work code 0x0B; does not dispatch a pane Event.
Darkages.exe:0x0045BDC0win_main_window_procLRESULT __stdcall(HWND window, UINT message, WPARAM wparam, LPARAM lparam)Route all explicit DAClass messages.Registered by app_win_main; recognizes the 15 values in the table above and delegates all others.
Darkages.exe:0x00498430app_virus_check_module_callbackint __cdecl(void *process_context, void *module_context, const char *module_name, FILE *log_stream)Report the first infected module to the window thread.Called through the loaded module scanner; posts WM_USER + 0x2046.
Darkages.exe:0x00498780app_virus_check_threadvoid __cdecl(char *log_filename)Run virus checking on a worker thread.Invokes loaded process and module enumerators, cleans up, then calls _endthread.
Darkages.exe:0x0049DA30render_screen_pane_load_palettevoid __thiscall(void *screen_pane, const char *palette_name)Load and apply a 256-entry palette.WM_PALETTECHANGED passes legend.pal; other UI transitions also call it.
Darkages.exe:0x0049E820ui_screen_pane_set_cursor_positionvoid __thiscall(void *screen_pane, int mouse_y, int mouse_x)Update the 32 by 32 software-cursor rectangle.Called by the shared mouse-message path while holding the screen-pane critical section.

UI Panes and Registries

The client represents screens, dialogs, controls, and many temporary overlays as Pane-derived objects. A pane participates in two independent hierarchies. The Screen composition tree records placement and parent-child drawing relationships. The event-dispatch tree records which panes receive mouse, keyboard, socket-packet, and timer callbacks. A pane may appear in both trees, but the trees have different nodes, payloads, and ownership.

The executable does not expose usable Microsoft C++ class RTTI for these pane types. Enumeration instead starts from the common Pane virtual layout. Every confirmed pane-compatible virtual table contains ui_pane_register_events at slot +0x30 and ui_pane_unregister_events at +0x34. Cross-references to the registration slot identify 173 compatible tables. These include controls and construction-state tables as well as full visible screens, so the count is not a count of 173 independently visible windows. The complete address and handler inventory is in Pane Virtual Table Inventory.

High-level operation

Two pane hierarchies

The Screen composition tree answers where a pane is placed and which panes are below it. ui_screen_registry is a static pointer to its heap registry. The root node holds ui_screen_pane, the persistent 640 by 480 surface pane. Adding a pane supplies a rectangle, an optional pane to place it in front of, and an optional parent pane. Removing a node also removes its child hierarchy.

The event-dispatch tree answers which panes are eligible for input and internal events. It is reached through event_dispatcher + 0x68. Mouse events use virtual slot +0x38, keyboard events use +0x3C, socket-packet events use +0x40, and due pane timers use +0x44. Event traversal visits a node’s children before the node itself. Mouse coordinates are adjusted into pane-relative coordinates during traversal.

The two registrations are deliberately separate. ui_pane_add_to_screen does not register event handling, and ui_pane_register_events does not add anything to the drawing tree. Constructors and transitions normally perform both operations explicitly.

Pane lifecycle

app_initialize first creates the Screen registry, constructs ui_screen_pane, and makes that pane the Screen root. It then creates and starts the event dispatcher and registers the screen pane as the initial event receiver.

The first substantial screen is TerminalPane. Its constructor publishes ui_terminal_pane, adds the pane below ui_screen_pane, registers it in the event hierarchy, and adds its controls. A successful terminal bootstrap packet causes ui_terminal_handle_server_packet to remove the terminal child, unregister and remove the terminal pane, create MainMenuPane, add and register the main menu, send the bootstrap/version traffic, and delete the terminal object. The terminal destructor clears its static pointer.

MainMenuPane publishes ui_main_menu_pane. Its server handler can create ServerSelectDialogPane as a registered child dialog. The dialog destructor explicitly unregisters the pane and removes it from Screen before destroying its dialog state.

The transition into the game reaches ui_main_menu_create_game_panes. It creates the persistent in-game BackgroundPane and MapPane, publishes ui_background_pane and ui_map_pane, and builds the larger child-pane graph below them. The map pane becomes the principal socket-packet receiver for in-game server actions.

Temporary controls follow the same rule. The game-buttons owner stores each allocated skill or spell pane in an indexed heap-pointer array, adds it below the owner in Screen, and registers it below the owner in the event tree. Removal deletes the node from both trees, deletes the object, and clears the pointer slot. TextEdit Escape and Enter paths also remove both registrations before queueing deferred deletion. Enter calls the class-specific virtual slot +0x4C before removal.

Visibility is separate from both hierarchies. Common Pane vslot +0x14 sets the visible flag at +0xB0 and invalidates the pane, while +0x18 clears the flag and invalidates the exposed parent region. Persistent content panes can therefore remain present in both registries while hidden. The exact packet and input transitions for equipment, status, skills, spells, users, paper, messages, and bulletin or mail are in UI, Input, and Packet Flows.

The base ui_pane_dtor only cancels timers and destroys base members. It does not unregister the object from either tree. Every owning path must therefore remove both registrations before immediate or deferred deletion. The observed order varies by owner, but both removals precede destruction.

Named persistent panes and handlers

The following panes have established construction, virtual-table, and handler evidence. M, K, S, and T mean mouse, keyboard, socket packet, and timer.

PaneStatic root and object sizeVtableM handlerK handlerS handlerT handler
ScreenPaneui_screen_pane at Darkages.exe:0x004F51C8, 0x580 bytesui_screen_pane_vtable at Darkages.exe:0x00524CE0ui_pane_default_mouse_handlerui_screen_pane_handle_key_eventui_pane_default_socket_handlerui_screen_pane_handle_timer
TerminalPaneui_terminal_pane at Darkages.exe:0x004FD640, 0xF78 bytesui_terminal_pane_vtable at Darkages.exe:0x00529000ui_terminal_handle_mouse_eventui_terminal_handle_key_eventui_terminal_handle_server_packetui_pane_default_timer_handler
MainMenuPaneui_main_menu_pane at Darkages.exe:0x004F51AC, 0x124 bytesui_main_menu_pane_vtable at Darkages.exe:0x005177C0ui_main_menu_handle_mouse_eventui_main_menu_handle_key_eventui_main_menu_handle_server_packetui_pane_default_timer_handler
BackgroundPaneui_background_pane at Darkages.exe:0x004F51B4, 0x144 bytesui_background_pane_vtable at Darkages.exe:0x00500700ui_background_pane_handle_mouse_eventui_pane_default_key_handlerui_handle_server_request_portraitui_background_pane_handle_timer
MapPaneui_map_pane at Darkages.exe:0x004F51B0, 0xF38 bytesui_map_pane_vtable at Darkages.exe:0x00519280ui_map_handle_mouse_eventui_map_handle_key_eventui_map_dispatch_server_packetui_map_handle_timer
ServerSelectDialogPaneDynamic, 0x554 bytesui_server_select_dialog_pane_vtable at Darkages.exe:0x00526140ui_server_select_handle_mouse_eventui_dialog_handle_key_eventui_server_select_handle_server_packetui_dialog_default_timer_handler
OptionPaneDynamic, 0x578 bytesui_option_pane_vtable at Darkages.exe:0x00520660ui_option_pane_handle_mouse_eventui_option_pane_handle_key_eventui_option_pane_handle_server_packetui_option_pane_handle_timer
Exit-wait paneDynamicui_exit_wait_pane_vtable at Darkages.exe:0x005207E0ui_dialog_handle_mouse_eventui_dialog_handle_key_eventui_exit_wait_handle_server_packetui_dialog_default_timer_handler

Friendly class names and concrete event coverage

The executable has no usable Microsoft RTTI for this hierarchy, but it retains many class and method diagnostics. A friendly class name is treated as confirmed only when the same constructor, destructor, or virtual method also installs or is stored in the candidate vtable. This establishes names such as BulletinDialogPane, ArticleListDialog, MailListPane, LegendDialogPane, MonsterPane2, EffectObjectPane, OptionPane, and WeatherPane without relying on nearby text alone. The complete confirmed name-to-vtable map is in Pane Virtual Table Inventory.

A vtable identifies the broad Event families a pane can receive. The handler body supplies the narrower filter. For example, ServerSelectDialogPane has mouse, keyboard, socket, and timer slots, but its mouse override only adds behavior for a left double-click and its socket override only claims server action 0x56. The inherited Dialog handlers still process their ordinary mouse and keyboard cases. This distinction is important for runtime hooks because a non-default slot does not imply that every subtype is consumed.

The client dispatches key-down Events only. Key-up updates EventMan state but does not enter the pane hierarchy. Timer callbacks are also separate from the numbered Event types: the timer worker calls pane slot +0x44 with a class-local callback identifier and two payload values.

The socket handler is not a Winsock callback. Socket bytes are decoded and converted to internal Event type 9 before the dispatcher calls pane slot +0x40. The packet path is documented in Networking, while Win32 WM_* handling remains in Input and Windows Events.

Runtime observation roots

The documented virtual addresses use image base 0x00400000. A runtime tool that resolves a loaded module should use loaded_module_base + RVA rather than assuming the preferred base.

VARVACurrent IDA nameRuntime meaningLifetime and nullability
Darkages.exe:0x004F51AC0x000F51ACui_main_menu_panePointer to the heap MainMenuPane.Non-null during the main-menu phase; cleared during transitions and teardown.
Darkages.exe:0x004E2D700x000E2D70ui_bulletin_sessionPointer to the active heap BulletinSession.Non-null only while a bulletin or mail session owns its child-dialog history.
Darkages.exe:0x004E32A40x000E32A4ui_equip_panePointer to the persistent heap User Equip pane.Published during game-pane construction and cleared by its destructor.
Darkages.exe:0x004E35640x000E3564ui_users_dialog_panePointer to the heap Users Dialog Pane.Null until the first CWho UI action creates it; reused for later SShowUsers replies.
Darkages.exe:0x004F51B00x000F51B0ui_map_panePointer to the heap MapPane.Published after game-pane creation; cleared during game teardown.
Darkages.exe:0x004F51B40x000F51B4ui_background_panePointer to the heap BackgroundPane.Published with the game UI; cleared during game teardown.
Darkages.exe:0x004F51C80x000F51C8ui_screen_panePointer to the persistent heap ScreenPane.Non-null after successful initialization until application shutdown.
Darkages.exe:0x004F51CC0x000F51CCui_screen_registryPointer to the heap Screen hierarchy list.Non-null after Screen initialization until application shutdown.
Darkages.exe:0x004F51D00x000F51D0event_dispatcherPointer to the dispatcher; +0x68 points to its pane hierarchy.Non-null after event initialization until dispatcher shutdown.
Darkages.exe:0x004FD6400x000FD640ui_terminal_panePointer to the heap TerminalPane.Published by the constructor and cleared by the destructor.

These roots are useful object discriminators as well as pointer chains. A live object begins with its current virtual-table pointer. Comparing that pointer against the vtable inventory identifies compatible panes without relying on absent RTTI.

The Runtime UI Memory Map turns these roots into concrete Screen and Event tree-walking recipes and records confirmed fields for GameButtonsPane, equipment and self-look state, skill and spell slots, BulletinSession, and Users Dialog Pane.

External readers can race Screen changes, event registration, and deferred deletion. The Screen add and remove paths do not acquire a visible lock around the packed list. A practical snapshot should read the list pointer, count, array pointer, and stride, copy the nodes, then re-read those four values and discard the snapshot if they changed. Each pane pointer should also be checked for readability and a known vtable before reading fields. Writes are more hazardous because pane fields, both registries, timers, and ownership must remain consistent.

Code-level flow

Common Pane layout and virtual slots

ui_pane_ctor constructs the common Pane base and installs ui_pane_vtable. The following fields and slots are directly established.

Pane offsetWidthEstablished meaning
+0x00004Current virtual-table pointer.
+0x00084Last propagated Screen or dispatcher error.
+0x003816Local graphic bound rectangle in client order: top, left, bottom, right.
+0x00A88Relative origin point: signed top at +0xA8 and left at +0xAC.
+0x00B01Visible or active state; set by ui_pane_show, cleared by ui_pane_hide, and queried by ui_pane_is_visible.
+0x00B11Constructor flag copied from the first stack argument; exact meaning is not yet established.
+0x00B21Constructor flag copied from the second stack argument; exact meaning is not yet established.
+0x00B31Pane flag copied into the Screen node payload.
+0x00F01Capture-related state checked before Screen removal.
Vtable offsetRoleBase implementation
+0x14Show pane and invalidate its regionui_pane_show
+0x18Hide pane and invalidate the exposed parent regionui_pane_hide
+0x28Add pane to Screen compositionui_pane_add_to_screen
+0x2CRemove pane from Screen compositionui_pane_remove_from_screen
+0x30Register event receiverui_pane_register_events
+0x34Unregister event receiverui_pane_unregister_events
+0x38Mouse Event handler, Event types 0 through 7ui_pane_default_mouse_handler
+0x3CKeyboard Event handler, Event type 8ui_pane_default_key_handler
+0x40Socket-packet Event handler, Event type 9ui_pane_default_socket_handler
+0x44Pane timer callbackui_pane_default_timer_handler
+0x48Additional class-specific handlerBase is nullsub_6; the generic role is not yet established.
+0x4COptional class-specific handlerAbsent in the base table; TextEdit calls its override on Enter.

Event values and handler filters

The dispatcher reads the byte at Event offset +0x0C. Mouse subtype values and their Win32 production paths are established in Input and Windows Events.

Event typeMeaningPane virtual slot and notes
0Mouse move+0x38
1Left single-button down+0x38
2Left double-button down+0x38; recognized by EventMan, not WM_LBUTTONDBLCLK
3Left-button up+0x38
4Right single-button down+0x38
5Right double-button down+0x38; recognized by EventMan, not WM_RBUTTONDBLCLK
6Right-button up+0x38
7Mouse wheel+0x38; payload is the signed wheel delta divided by 120
8Mapped key down+0x3C; there is no pane-dispatched key-up Event
9Decoded socket packet or terminal bootstrap bytes+0x40

The following filters are comparisons in the named handler bodies. A handler that delegates to ui_dialog_handle_mouse_event can still inherit the generic Dialog cases after its class-specific work.

Class or shared handlerAccepted mouse subtype valuesEstablished behavior
ui_dialog_handle_mouse_event0, 1, 2, 3, 4, 7Generic Dialog hover, button, drag, and wheel processing. There is no explicit subtype 5 or 6 branch.
ui_bulletin_dialog_handle_mouse_eventAdds 1 and 3, then delegatesCaptures and releases Bulletin-family dialog drag state.
LegendDialogPaneAdds 1 and 4, then delegatesCaptures position on left down and invokes its class action on right down.
BackPane1Starts its button animation or help-button timer.
MainMenuPane0, 3Tracks menu hover and activates the released selection.
QuestionMessageDialog and QuestionMessageFaceDialogAdds 1 and 3, then delegatesCaptures and releases dialog drag state.
ServerSelectDialogPaneAdds 2, then delegatesSelects one of five server rows on left double-click.
TerminalPane1, 3Presses and releases its three connection controls.

Keyboard handlers receive Event type 8 and compare the mapped key byte at Event offset +0x10. Values below are recorded as the handler sees them. They are not assumed to be raw WM_* virtual-key values.

Class or shared handlerExplicit mapped key valuesEstablished behavior
ui_dialog_handle_key_event0x09, 0x0D, 0x20, 0x1B, 0x90Tab, Enter, Space, Escape, screen action 0x90, plus focused-control forwarding.
BulletinDialogPane and BoardListDialogQ/q, W/w, E/e, 0x90Shared Bulletin-family navigation and focused-control forwarding.
ArticleListDialogV/v, N/n, 0x1B, 0x84View, new-article, close, and class-specific list actions.
MailListDialogV/v, N/n, R/r, 0x1B, 0x84View, new, reply, close, and class-specific list actions.
NewArticleDialog and NewMailDialog0x09, 0x0D, 0x1BFocus traversal, submit through the focused control, and cancel.

Socket Event handlers filter on the first decoded byte or, where noted, a second subtype byte. The Map action set is kept in the protocol index because each branch has its own parser and side effects.

ReceiverClaimed socket inputNotes
Bulletin dialog familyServer action 0x31Calls the receiver’s class-specific virtual at +0x64.
MainMenuPaneServer actions 0x00, 0x02, 0x03, 0x0ABootstrap, version, and main-menu transitions.
BackPaneServer action 0x49Portrait request.
MapPane31 action bytes from 0x03 through 0x4BExact switch coverage is in Server Actions.
Exit-wait paneServer action 0x4C, subtype 1Completes the orderly-exit exchange.
ServerSelectDialogPaneServer action 0x56Parses the server table.
TerminalPaneBootstrap byte stream states 0 through 7Stateful terminal protocol, not an ordinary packet-action switch.

Timer callback identifiers are local to the receiving class. BackPane uses IDs 0, 1, and 2; MapPane and ScreenPane each dispatch IDs 0 through 4. These values share no global enum with Event types 0 through 9.

Packed hierarchy representation

Both registries use ui_hierarchy_list_ctor, which adds an 11-byte hierarchy header to the requested payload size. The result is a packed, commonly unaligned node array.

List offsetWidthMeaning
+0x0C4Node stride.
+0x144Node count.
+0x184Pointer to the contiguous packed node array.
+0x1C4Root or parent node pointer. Screen stores its separately allocated root here.
Node offsetWidthMeaning
+0x004Parent node pointer.
+0x044Child hierarchy-list pointer, or null.
+0x08variableRegistry-specific payload. Both mapped registries begin the payload with the pane pointer.

Screen requests a 0x34-byte payload, producing stride 0x3F. The pane pointer is at node +0x08, its RECT is at +0x0C, and copied pane flags begin at +0x1C. The root pointer stored at [ui_screen_registry + 0x1C] addresses a node whose child-list pointer is at root +0x04 and whose pane at root +0x08 is ui_screen_pane.

The event dispatcher requests a four-byte payload, producing stride 0x0F. Each event node therefore contains only its hierarchy links and the pane pointer at +0x08.

The exact external pointer chains are:

screen_list = read_process_u32(module_base + 0x000f51cc);
screen_root = read_process_u32(screen_list + 0x1c);
screen_children = read_process_u32(screen_root + 0x04);

dispatcher_object = read_process_u32(module_base + 0x000f51d0);
event_list = read_process_u32(dispatcher_object + 0x68);

Walk screen_children for ordinary Screen descendants. The separately allocated Screen root itself can be observed directly at screen_root. Walk event_list for registered event receivers.

A raw external walk is equivalent to:

static void walk_pane_list(uintptr_t list_address)
{
    uint32_t node_stride;
    uint32_t node_count;
    uintptr_t node_array;
    uint32_t node_index;

    node_stride = read_process_u32(list_address + 0x0c);
    node_count = read_process_u32(list_address + 0x14);
    node_array = read_process_u32(list_address + 0x18);

    for (node_index = 0; node_index < node_count; ++node_index) {
        uintptr_t node_address;
        uintptr_t child_list;
        uintptr_t pane_address;

        node_address = node_array + node_index * node_stride;
        child_list = read_process_u32(node_address + 0x04);
        pane_address = read_process_u32(node_address + 0x08);
        observe_pane(pane_address);

        if (child_list != 0) {
            walk_pane_list(child_list);
        }
    }
}

The helper reads 32-bit values from arbitrary byte addresses because the strides 0x3F and 0x0F do not preserve four-byte alignment.

Creation and removal paths

The common add path is:

  1. A constructor installs a concrete pane vtable after ui_pane_ctor initializes the base.
  2. The owner calls vslot +0x28, reaching ui_pane_add_to_screen and then ui_screen_add_pane.
  3. ui_screen_add_pane validates bounds, resolves optional parent and front panes with ui_screen_find_pane_node, copies the Screen payload, and inserts the packed node.
  4. The owner calls vslot +0x30, reaching ui_pane_register_events and event_dispatcher_register_pane.

Removal reverses both memberships before destruction. ui_pane_remove_from_screen releases capture-related state when necessary and calls ui_screen_remove_pane. Event removal reaches event_dispatcher_unregister_pane. Representative dynamic paths are ui_skill_inventory_remove_slot_pane, ui_spell_inventory_remove_slot_pane, the server-select destructor, and ui_text_edit_handle_key_event.

ui_screen_find_pane_node recursively returns the containing list and node index. ui_screen_get_pane_origin walks parent links to accumulate screen coordinates. These functions make the Screen hierarchy useful for both rendering and runtime inspection.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
Darkages.exe:0x0040A0A0ui_dialog_default_socket_handlerint __thiscall(void *, void *)Decline a socket Event.Default Dialog vslot +0x40; always returns zero.
Darkages.exe:0x0040A0B0ui_dialog_default_timer_handlerint __thiscall(void *, int, int, int)Decline a timer callback.Default Dialog vslot +0x44; always returns zero.
Darkages.exe:0x0040A5A0ui_pane_default_key_handlerestablished Pane virtual handlerDefault keyboard Event handler.Base vtable slot +0x3C; inherited by panes without a keyboard override.
Darkages.exe:0x0040A5B0ui_background_pane_ctorestablished __thiscall constructorConstruct the in-game background pane.Called by ui_main_menu_create_game_panes; object size is 0x144.
Darkages.exe:0x0040B460ui_pane_default_mouse_handlerestablished Pane virtual handlerDefault mouse Event handler.Base vtable slot +0x38.
Darkages.exe:0x0040B470ui_pane_default_socket_handlerestablished Pane virtual handlerDefault socket Event handler.Base vtable slot +0x40.
Darkages.exe:0x0040EA50ui_pane_default_timer_handlerestablished Pane timer handlerDefault timer callback.Base vtable slot +0x44.
Darkages.exe:0x00414130ui_bulletin_dialog_handle_key_eventint __thiscall(void *, void *)Process shared Bulletin-family key actions.Vslot +0x3C for BulletinDialogPane and BoardListDialog.
Darkages.exe:0x00414260ui_bulletin_dialog_handle_server_packetint __thiscall(void *, void *)Dispatch Bulletin-family server action 0x31.Vslot +0x40; calls class vslot +0x64.
Darkages.exe:0x004142E0ui_bulletin_dialog_handle_mouse_eventint __thiscall(void *, void *)Add Bulletin-family drag tracking.Vslot +0x38; handles subtypes 1 and 3, then delegates to Dialog.
Darkages.exe:0x00415A10ui_article_list_dialog_handle_key_eventint __thiscall(void *, void *)Process ArticleListDialog key actions.Handles mapped V/v, N/n, Escape, and 0x84.
Darkages.exe:0x004181A0ui_new_article_dialog_handle_key_eventint __thiscall(void *, void *)Process NewArticleDialog editing keys.Handles Tab, Enter, Escape, and focused controls.
Darkages.exe:0x004184C0ui_mail_list_dialog_handle_key_eventint __thiscall(void *, void *)Process MailListDialog key actions.Adds mapped reply keys R/r to the list-dialog actions.
Darkages.exe:0x0041AE70ui_new_mail_dialog_handle_key_eventint __thiscall(void *, void *)Process NewMailDialog editing keys.Handles Tab, Enter, Escape, and focused controls.
Darkages.exe:0x0041C4D0ui_legend_dialog_pane_ctorestablished __thiscall constructorConstruct the outer LegendDialogPane and its inner list control.Installs ui_legend_dialog_pane_vtable; the inner 0x00506BE0 class remains unnamed.
Darkages.exe:0x0041C770ui_legend_dialog_pane_handle_mouse_eventint __thiscall(void *, void *)Process LegendDialogPane mouse input.Adds subtypes 1 and 4, then delegates to Dialog.
Darkages.exe:0x00431150event_dispatcher_register_panevoid __thiscall(void *, void *, int, int)Insert a pane into the event hierarchy.Reached directly for the root and through Pane vslot +0x30.
Darkages.exe:0x004311B0event_dispatcher_unregister_panevoid __thiscall(void *, void *)Remove a pane from the event hierarchy.Reached through Pane vslot +0x34 and several explicit cleanup paths.
Darkages.exe:0x00431D54event_dispatch_hierarchyint __thiscall(void *, void *, void *)Traverse child-first and call the event-specific pane slot.Selects +0x38, +0x3C, or +0x40 for Event types 0 through 9.
Darkages.exe:0x004325D0event_is_mouseint __thiscall(void *event)Test Event type 0 through 7.Used by event_dispatch_hierarchy.
Darkages.exe:0x004325F0event_is_keyboardint __thiscall(void *event)Test Event type 8.Used by event_dispatch_hierarchy.
Darkages.exe:0x0042A190ui_dialog_handle_mouse_eventint __thiscall(void *, void *)Process generic Dialog mouse input.Explicit cases are 0, 1, 2, 3, 4, and 7.
Darkages.exe:0x0042AED0ui_dialog_handle_key_eventint __thiscall(void *, void *)Process generic Dialog key input and forward to the focused control.Handles Event type 8 only.
Darkages.exe:0x00442C04ui_skill_inventory_remove_slot_panevoid __thiscall(void *, uint8_t)Remove, unregister, delete, and clear an indexed skill pane.Counterpart to ui_skill_inventory_create_slot_pane.
Darkages.exe:0x00442D14ui_skill_inventory_create_slot_panevoid __thiscall(void *, uint8_t, uint16_t, const char *)Allocate and register an indexed 0x294-byte skill pane.Stores the heap pointer in its inventory parent.
Darkages.exe:0x00443E14ui_spell_inventory_remove_slot_panevoid __thiscall(void *, uint8_t)Remove, unregister, delete, and clear an indexed spell pane.Counterpart to ui_spell_inventory_create_slot_pane.
Darkages.exe:0x00443F24ui_spell_inventory_create_slot_panevoid __thiscall(void *, uint8_t, uint16_t, int8_t, const char *, const char *, uint8_t)Allocate and register an indexed 0x214-byte spell pane.Stores the heap pointer in its inventory parent.
Darkages.exe:0x00446090ui_point_initvoid __cdecl(void *, int, int)Initialize an eight-byte client point.Used for Pane relative origin.
Darkages.exe:0x004460B0ui_rect_initvoid __cdecl(void *, int, int, int, int)Initialize a client rectangle from left, top, right, and bottom.Stores the four fields in top, left, bottom, right memory order.
Darkages.exe:0x0044E4D0ui_hierarchy_list_ctorvoid *__thiscall(void *list, int payload_size)Construct a packed hierarchy list.Produces stride payload_size + 0x0B.
Darkages.exe:0x0044F4F0ui_hierarchy_remove_nodeestablished __thiscall removal methodRemove one packed node and its child list.Shared by Screen composition and event dispatch.
Darkages.exe:0x004594A0ui_hierarchy_get_nodevoid *__thiscall(void *list, int index)Return a checked packed-node address.Computes array + index * stride.
Darkages.exe:0x0045D930ui_main_menu_pane_ctorestablished __thiscall constructorConstruct and publish MainMenuPane.Called by terminal completion and the in-game return path.
Darkages.exe:0x0045FA44ui_main_menu_create_game_panesestablished __thiscall transition methodBuild and publish the in-game pane graph.Constructs BackgroundPane, MapPane, and child panes.
Darkages.exe:0x004658C0ui_map_pane_ctorestablished __thiscall constructorConstruct MapPane.Called for a 0xF38-byte allocation during game UI creation.
Darkages.exe:0x0047DBA0ui_question_message_dialog_handle_mouse_eventint __thiscall(void *, void *)Add drag tracking for the question-message dialog family.Handles subtypes 1 and 3, then delegates to Dialog.
Darkages.exe:0x0048D2E0ui_effect_object_pane_handle_timerestablished Pane timer handlerProcess EffectObjectPane timer callbacks.Class name and method are confirmed by the vslot and diagnostic.
Darkages.exe:0x0048F600ui_option_pane_ctorvoid *__thiscall(void *)Construct, add, and register dynamic OptionPane.Installed by the Q window-button path.
Darkages.exe:0x004909E0ui_option_pane_handle_server_packetint __thiscall(void *, void *)Claim SSelfSaveOk.Creates a local confirmation dialog.
Darkages.exe:0x00490A80ui_option_pane_handle_mouse_eventint __thiscall(void *, void *)Process OptionPane mouse state.Delegates common behavior to Dialog.
Darkages.exe:0x00490D70ui_option_pane_handle_timerint __thiscall(void *, int, int, int)Advance OptionPane timer state.Handles callback 100 separately.
Darkages.exe:0x00490FE0ui_option_pane_handle_key_eventint __thiscall(void *, void *)Process OptionPane shortcuts.X constructs the exit-wait pane.
Darkages.exe:0x00493240ui_pane_get_bound_rectvoid __thiscall(void *, int32_t *)Return the local graphic rectangle translated by Pane origin.Reads +0x38 and +0xA8.
Darkages.exe:0x004932B0ui_pane_set_bound_rectvoid __thiscall(void *, const int32_t *)Store relative origin and apply a translated local rectangle.Vtable slot +0x24; Screen add calls it.
Darkages.exe:0x00498F20ui_screen_registry_ctorvoid *__thiscall(void *registry)Construct the Screen composition registry.Requests a 0x34-byte hierarchy payload.
Darkages.exe:0x00498F40ui_screen_add_paneestablished __thiscall add methodValidate and insert a Screen pane node.Called by ui_pane_add_to_screen.
Darkages.exe:0x004993C4ui_screen_find_pane_nodeestablished recursive __thiscall searchFind a pane and optionally return its list and index.Used for parent, sibling, removal, and coordinate queries.
Darkages.exe:0x00499530ui_screen_remove_paneestablished __thiscall removal methodRemove a pane node and child hierarchy.Called by ui_pane_remove_from_screen.
Darkages.exe:0x00499ED0ui_screen_get_pane_originestablished __thiscall queryAccumulate a pane’s screen-space origin.Walks Screen hierarchy parent links.
Darkages.exe:0x004A1680ui_scrollable_pane_handle_mouse_eventint __thiscall(void *, void *)Route mouse input through scroll bars and the list body.Base mouse path for ArticleListPane, MailListPane, and ItemListPane.
Darkages.exe:0x004A1900ui_scrollable_pane_handle_key_eventint __thiscall(void *, void *)Route key input through scroll bars and the list body.Base key path for the same scrollable list panes.
Darkages.exe:0x00492A90ui_pane_dtorvoid __thiscall(void *pane)Cancel timers and destroy the Pane base.Does not remove either hierarchy registration.
Darkages.exe:0x00492AD0ui_pane_ctorvoid *__thiscall(void *, unsigned char, unsigned char)Construct the common Pane base.Installs ui_pane_vtable and initializes bounds and flags.
Darkages.exe:0x00492C10ui_pane_is_visibleint __thiscall(void *pane)Query common Pane visibility.Used by the equipment same-pane toggle.
Darkages.exe:0x00492C40ui_pane_showvoid __thiscall(void *pane)Set +0xB0, update Screen state, and invalidate.Common Pane vslot +0x14.
Darkages.exe:0x00492D50ui_pane_hidevoid __thiscall(void *pane)Clear +0xB0 and invalidate the exposed parent region.Common Pane vslot +0x18; also handles capture state.
Darkages.exe:0x004933C0ui_pane_add_to_screenvoid __thiscall(void *, const int32_t *, void *, void *)Add this pane to Screen composition using a client-order rectangle.Pane vslot +0x28; delegates to ui_screen_add_pane.
Darkages.exe:0x00493480ui_pane_remove_from_screenvoid __thiscall(void *)Remove this pane from Screen composition.Pane vslot +0x2C; delegates to ui_screen_remove_pane.
Darkages.exe:0x00493530ui_pane_register_eventsvoid __thiscall(void *, void *, int)Register this pane for event traversal.Pane vslot +0x30; the signature xrefs enumerate compatible vtables.
Darkages.exe:0x004935D0ui_pane_unregister_eventsvoid __thiscall(void *)Unregister this pane from event traversal.Pane vslot +0x34.
Darkages.exe:0x004A1B04ui_server_select_dialog_pane_dtorestablished __thiscall destructorUnregister and remove the server-select dialog.Restores main-menu server-selection state before base destruction.
Darkages.exe:0x004A2740ui_server_select_dialog_pane_ctorestablished __thiscall constructorConstruct, add, and register the server-select dialog.Called by ui_main_menu_handle_server_packet.
Darkages.exe:0x004AC944ui_terminal_pane_dtorestablished __thiscall destructorClear the terminal root and destroy its child editors.Calls ui_pane_dtor after derived cleanup.
Darkages.exe:0x004ACA00ui_terminal_pane_ctorestablished __thiscall constructorConstruct, publish, add, and register TerminalPane.Object size is 0xF78.
Darkages.exe:0x004AD130ui_get_terminal_panevoid *__cdecl(void)Return ui_terminal_pane.Direct static-root accessor.
Darkages.exe:0x004AD140ui_terminal_handle_server_packetestablished Pane socket handlerHandle terminal packets and transition to MainMenuPane.Terminal vtable slot +0x40.
Darkages.exe:0x004BE6F4ui_text_edit_handle_key_eventestablished Pane keyboard handlerComplete or cancel a temporary text editor.Removes both registrations and queues deferred deletion.

Internal Event Routing

The internal event system owns two related paths. It routes typed events through registered panes, and it runs periodic timer callbacks on a worker thread independently of the blocking Windows message pump. Network packets enter the same pane hierarchy through a separate event-manager worker.

High-level operation

Dispatcher, event manager, and pane hierarchy

app_initialize creates the global event_dispatcher, starts its worker, then constructs and starts the separate event_manager_instance. The dispatcher owns the pane registry and the timer list. The event manager converts queued work records into Event objects, including socket-packet events, and submits those events to the dispatcher.

The established socket path is:

  1. The Socket receive worker posts copied decoded bytes through event_post_socket_bytes.
  2. event_process_work_item converts the queued record to Event type 9.
  3. event_queue_socket_packet transfers the owned packet pointer and size into the Event queue.
  4. event_dispatch and event_dispatch_hierarchy walk registered panes and invoke the socket-event virtual handler.

Packet ownership and the type 9 fields are documented in Networking. Pane registration and timer callbacks remain owned by this subsystem.

The dispatcher hierarchy is separate from the Screen composition hierarchy. The event tree stores packed 0x0F-byte nodes with a pane pointer at +0x08, while Screen stores placement and drawing relationships in 0x3F-byte nodes. Their raw layouts, pane virtual slots, creation and removal flows, and complete vtable inventory are documented in UI Panes and Registries.

Window input uses the same EventMan worker. The main window procedure posts work codes 4 through 0x0B for mouse movement, buttons, wheel, and physical scan codes. event_process_work_item converts those records into mouse and key state or Events before the normal pane hierarchy sees them. The complete mapping is documented in Input and Windows Events.

Event copying and queue ownership

event_dispatcher_queue_event_copy allocates and shallow-copies the complete 0x24-byte Event, then posts dispatcher work code 3. event_dispatcher_process_work_item calls event_dispatch for that code. After dispatch returns, it frees Event +0x14 when the object is a socket Event, then deletes the Event copy. This dispatcher-worker cleanup still runs when a hook or pane consumes the Event.

event_manager_queue_event_copy performs the same 0x24-byte shallow copy but posts EventMan work code 0x0F. event_process_work_item forwards that copy into the dispatcher queue. No native call site for the EventMan wrapper is present in this build, so its implementation and consumer are established but its original producer role is not.

Both wrappers ultimately call util_thread_queue_post_async, which appends the raw {code, data, value} record and releases the queue semaphore. The raw helper does not copy the data pointer. Ownership comes from the caller-specific wrapper, so proxy code should use a mapped wrapper instead of posting a stack pointer directly. See Event Proxy Hooks and Injection for the resulting interception and injection boundaries.

Bounded queue synchronization

The worker queue is safe for simultaneous producers. Its ring owns a monitor, a not-full condition, and a not-empty condition. util_ring_buffer_push_wait acquires the monitor, waits when element_count == capacity, copies one fixed-size record, signals not-empty, and releases the monitor. util_ring_buffer_pop_wait performs the symmetric operation and signals not-full after removing a record.

This establishes synchronization for the native client and an injected producer that calls a copied-input wrapper at the same time. It does not make admission nonblocking. util_thread_queue_post_async does not wait for worker completion, but its internal ring push can wait indefinitely for space. The Socket and EventMan rings each hold 128 records; the dispatcher ring holds 1,024.

That distinction matters during shutdown. EventMan destruction deletes its owned Socket, clears both static roots, and then tears down its worker base. The worker-base destructor uses TerminateThread and destroys the ring and its synchronization objects. A foreign thread must not still be waiting in that ring when teardown begins. The Event Proxy design therefore uses bounded proxy queues and worker-affine pumps, with app_shutdown entry as a drain barrier.

Worker and timer lifecycle

The Windows message loop is not a frame loop. The dispatcher derives from a cross-subsystem thread-queue base whose constructor creates a thread with _beginthreadex and CREATE_SUSPENDED. The same base is also used by the Socket and other worker-derived objects. The dispatcher configures that worker before util_thread_queue_start resumes it:

SettingEstablished value or rule
Work-queue capacity0x400 records
WaitForMultipleObjects timeout1 millisecond
Requested multimedia timer period5 milliseconds clamped to TIMECAPS.wPeriodMin..wPeriodMax
Timer clock32-bit timeGetTime() milliseconds
Initial next deadlineUINT32_MAX
Timer-list capacity256 records
Timer-record size20 bytes
Worker priorityTHREAD_PRIORITY_TIME_CRITICAL

The dispatcher calls timeGetDevCaps but does not check its return. It calls timeBeginPeriod with the selected period, stores the first timeGetTime sample, creates the timer list, and raises the still-suspended worker’s priority. A 1 millisecond wait timeout is therefore a polling interval requested from the worker, not a promise that Windows will deliver callbacks every millisecond. The multimedia period is normally 5 milliseconds when the device range permits it.

util_thread_queue_start first calls virtual slot +0x08 so the derived object can add its wait object. It resumes the thread only when app_error_code remains zero.

The worker runs WaitForMultipleObjects(wait_count, wait_handles, FALSE, timeout_ms) forever. Wait handle index 0 is the queued-work notification. A work wake first checks the ring. It removes and handles one record when nonempty, but an empty ring is also valid and skips directly to the periodic virtual. Other registered handle indices call virtual slot +0x1C. After every handled wake, empty queue wake, secondary signal, or timeout, the loop calls virtual slot +0x10. In the dispatcher vtable, that periodic slot is event_dispatcher_tick; the Socket slot is net_poll_receive; the EventMan slot is event_manager_periodic_noop.

Timer records and scheduling

Timer insertion normally reaches event_dispatcher_process_work_item as work code 4. Code 5 cancels all timers for a pane. event_dispatcher_insert_timer converts the relative delay to an absolute 32-bit deadline by adding it to the dispatcher’s most recent timeGetTime sample.

Each sorted timer record has this layout:

OffsetWidthMeaning
+0x004Receiver pane pointer
+0x044Callback identifier
+0x084Absolute timeGetTime deadline
+0x0C4First callback payload value
+0x104Second callback payload value

The insertion scan uses an unsigned comparison and places the record before the first later deadline. Equal deadlines remain in insertion order. If the new record becomes element 0, its deadline is copied to the dispatcher’s cached next-deadline field.

event_dispatcher_remove_pane_timers scans the complete list and removes every record whose receiver pointer matches the pane. It then sets the cached deadline from the new first record, or to UINT32_MAX when the list is empty.

Periodic tick and deferred deletion

Each event_dispatcher_tick first drains event_deferred_delete_queue. The drain walks queued object pointers from the last element to the first, calls each object’s deleting destructor with flag 1, and removes the entry. This gives client code a deferred-destruction path tied to the dispatcher worker rather than the Windows message thread.

The tick then samples timeGetTime, stores the result, and compares it to the cached next deadline as unsigned 32-bit values. If the current tick is earlier, the tick returns. Otherwise it removes only the first timer record, refreshes the cached deadline, verifies that the receiver is still an active object, and calls receiver virtual slot +0x44 with the callback identifier and the two payload values.

At most one due timer is dispatched per worker iteration. When several timers are overdue, they are removed across successive worker wakes or timeouts.

The code uses plain unsigned addition, sorting, and current_tick < deadline comparisons. It does not use a signed modular difference or another visible timeGetTime wraparound correction. Implementations that need byte-for-byte behavioral compatibility should preserve that distinction; implementations seeking a robust long-running clock should account for 32-bit wrap separately.

Shutdown and ownership

app_shutdown first deletes EventMan, whose destructor deletes its owned Socket and clears both static roots. It later deletes the event dispatcher before it deletes the global deferred-deletion queue. The dispatcher destructor performs these operations in order:

  1. Delete the pane registry.
  2. Delete the timer list.
  3. Call timeEndPeriod with the stored period.
  4. Invoke the worker-base destructor.

The worker-base destructor calls TerminateThread(worker_handle, 0), closes the thread handle and every wait handle, and destroys the queued message and synchronization objects. There is no cooperative stop flag, worker acknowledgement, or thread join. The pane and timer containers are released before the worker is forcibly terminated, so shutdown relies on the fixed surrounding teardown sequence and timing rather than an explicit worker quiescence handshake.

Code-level flow

Construction and start

  1. Darkages.exe:0x0045CCA0 app_initialize allocates the 128-byte dispatcher object and calls event_dispatcher_ctor.
  2. Darkages.exe:0x00430FE0 calls util_thread_queue_ctor(this, 0x400). The base constructor creates queue state and a suspended _beginthreadex worker stored at object offset +0x60.
  3. The dispatcher installs event_dispatcher_vtable, calls util_thread_queue_set_wait_timeout(this, 1), and allocates its pane registry at +0x68.
  4. It reads TIMECAPS, clamps 5 milliseconds into the reported range, stores the result at +0x6C, and calls timeBeginPeriod.
  5. It stores timeGetTime() at +0x70, sets cached next deadline +0x74 to UINT32_MAX, and creates a 256-element timer list at +0x78 with 20-byte elements.
  6. It sets the suspended thread to THREAD_PRIORITY_TIME_CRITICAL.
  7. app_initialize stores the object in event_dispatcher and calls util_thread_queue_start, which creates the derived wait object and resumes the worker.
  8. After the screen pane exists, event_dispatcher_register_pane(screen_pane, 0, 0) inserts it into the pane registry.
  9. app_initialize separately allocates the EventMan object, calls event_manager_ctor, and calls util_thread_queue_start. The constructor uses a work-queue capacity of 0x80, publishes event_manager_instance, and sets its worker timeout from the constructed event-manager state.

Allocation failure sets app_error_code to 2 and records the same error in the dispatcher base. The initializer then follows its staged failure cleanup.

Worker loop

Darkages.exe:0x004BF250 util_thread_queue_worker_loop has no normal exit branch. Its repeating path is equivalent to:

for (;;) {
    uint32_t wait_result;

    wait_result = WaitForMultipleObjects(
        worker->wait_count,
        worker->wait_handles,
        0,
        worker->timeout_ms
    );

    if (wait_result == WAIT_OBJECT_0) {
        if (!util_ring_buffer_is_empty(worker->work_queue)) {
            process_one_queued_record(worker);
        }
    } else if (wait_result > WAIT_OBJECT_0 &&
               wait_result < WAIT_OBJECT_0 + worker->wait_count) {
        process_secondary_wait_handle(worker, wait_result);
    }

    run_periodic_worker_method(worker);
}

The real queue path supports both asynchronous records and synchronous records whose producer waits on an event for a returned result. After processing a synchronous record, the worker writes the result to the matching waiter entry and signals its event.

Timer insertion and callback

For work code 4, event_dispatcher_process_work_item extracts the receiver, callback identifier, delay, and two payload values and calls event_dispatcher_insert_timer. The insertion function builds the 20-byte record, performs a linear scan of the sorted list, inserts it, and updates +0x74 only when inserted at index 0.

The periodic vtable call reaches event_dispatcher_tick:

event_deferred_delete_queue_drain(event_deferred_delete_queue);

current_tick = timeGetTime();
dispatcher->current_tick = current_tick;
if (current_tick < dispatcher->next_deadline) {
    return;
}

timer_record = remove_first_timer_record(dispatcher);
refresh_next_deadline(dispatcher);

if (receiver_is_active(timer_record.receiver)) {
    invoke_timer_callback(
        timer_record.receiver,
        timer_record.callback_id,
        timer_record.payload_0,
        timer_record.payload_1
    );
}

The implementation removes the timer before checking receiver activity. An inactive receiver therefore loses the due callback rather than retaining or rescheduling it.

Network event handoff

The separate event manager’s mapped packet route is documented in detail under receive and event routing. The important cross-thread ownership boundary is that event_post_socket_bytes copies the decoded packet into a work record. event_process_work_item later constructs the Event and queues it to the dispatcher. The dispatcher worker frees the owned packet and Event copy after pane dispatch.

Dispatcher object fields

Offsets below are from the event dispatcher object. The worker-base fields are shared with the event manager and other worker-derived objects.

OffsetWidthWorking fieldReads and writes
+0x004vtableSet to event_dispatcher_vtable.
+0x0C4wait_timeout_msSet to 1 by util_thread_queue_set_wait_timeout; consumed by util_thread_queue_worker_loop.
+0x101wait_handle_countPassed to WaitForMultipleObjects.
+0x14variablewait_handlesHandle index 0 is the worker queue notification.
+0x544work_queueBounded fixed-record ring; capacity is 0x400 for the dispatcher and 0x80 for EventMan and Socket.
+0x584completion_monitorSerializes synchronous completion bookkeeping.
+0x5C4completion_waitersTracks synchronous callers waiting for a work result.
+0x604worker_threadSuspended handle created by the base constructor, resumed by util_thread_queue_start, forcibly terminated by the base destructor.
+0x644worker_thread_idThread identifier returned by _beginthreadex.
+0x684pane_registryPoints to a packed hierarchy with stride 0x0F; each node stores its child list at +0x04 and pane at +0x08.
+0x6C4multimedia_period_msPeriod passed to timeBeginPeriod and later timeEndPeriod.
+0x704current_tickLatest timeGetTime sample; also the base for newly inserted relative delays.
+0x744next_deadlineFirst timer deadline, or UINT32_MAX when empty.
+0x784timer_listSorted list of 20-byte timer records.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
Darkages.exe:0x0040E4D0event_deferred_delete_queue_ctorvoid *__thiscall(void *queue_object)Construct the deferred-deletion queue.Called by app_initialize; source diagnostics identify the original implementation unit as BlackHole.cpp.
Darkages.exe:0x0040E5C0event_deferred_delete_queue_drainvoid __thiscall(void *queue_object)Delete and remove every deferred object.Called first by each event_dispatcher_tick and during shutdown before queue deletion.
Darkages.exe:0x00430F84event_dispatcher_dtorvoid __thiscall(void *event_dispatcher_object)Release dispatcher containers and multimedia timing state.Calls timeEndPeriod, then util_thread_queue_dtor.
Darkages.exe:0x00430FE0event_dispatcher_ctorvoid *__thiscall(void *event_dispatcher_object)Construct the dispatcher, timer list, and worker timing state.Called by app_initialize; calls util_thread_queue_ctor, util_thread_queue_set_wait_timeout, timeBeginPeriod, and SetThreadPriority.
Darkages.exe:0x00431150event_dispatcher_register_panevoid __thiscall(void *event_dispatcher_object, void *pane, int hierarchy, int position)Add a pane and hierarchy metadata to the dispatcher registry.Called by app_initialize for the screen pane and by later pane lifecycle paths.
Darkages.exe:0x004311B0event_dispatcher_unregister_panevoid __thiscall(void *event_dispatcher_object, void *pane)Remove a pane from the dispatcher registry.Prevents later hierarchy dispatch to the removed pane.
Darkages.exe:0x00431200event_dispatcher_queue_event_copyvoid __thiscall(void *event_dispatcher_object, const void *event)Copy one 0x24-byte Event into dispatcher work.Allocates the copy and posts work code 3; the dispatcher worker owns final cleanup.
Darkages.exe:0x00431320event_dispatcher_insert_timervoid __thiscall(void *event_dispatcher_object, void *receiver, int callback_id, unsigned int delay_ms, int payload_0, int payload_1)Insert a relative timer into the absolute-deadline sorted list.Reached from dispatcher work code 4; updates cached deadline when inserted first.
Darkages.exe:0x00431470event_dispatcher_remove_pane_timersvoid __thiscall(void *event_dispatcher_object, void *receiver)Cancel every timer owned by one pane.Reached from dispatcher work code 5; refreshes the cached deadline.
Darkages.exe:0x004315B0event_dispatcher_tickvoid __thiscall(void *event_dispatcher_object)Drain deferred deletion and dispatch at most one due timer.Dispatcher vtable slot +0x10; called by util_thread_queue_worker_loop after every wake or timeout.
Darkages.exe:0x004316E0event_dispatcher_process_work_itemvoid __thiscall(void *event_dispatcher_object, int code, void *data, int value)Execute dispatcher work codes 1 through 5.Timer insertion is code 4; pane-timer cancellation is code 5.
Darkages.exe:0x00431B84event_dispatchint __thiscall(void *this, void *event)Dispatch one internal Event.Called by the event manager; delegates hierarchy traversal to event_dispatch_hierarchy.
Darkages.exe:0x00431D54event_dispatch_hierarchyint __thiscall(void *this, void *event, void *hierarchy)Walk registered panes and call the type-specific virtual handler.Socket Event type 9 reaches pane virtual slot +0x40.
Darkages.exe:0x00432630event_manager_ctorvoid *__thiscall(void *event_manager_object)Construct and publish the EventMan singleton.Calls util_thread_queue_ctor with capacity 0x80, initializes event-manager and Socket-facing state, and stores event_manager_instance.
Darkages.exe:0x00432514event_manager_dtorvoid __thiscall(void *event_manager_object)Destroy EventMan and its owned Socket.Clears net_socket_instance and event_manager_instance, then tears down the worker base.
Darkages.exe:0x00432E50event_post_socket_bytesvoid __thiscall(void *this, const uint8_t *packet, int length)Copy decoded packet bytes to event-manager work code 0x0E.Called by the Socket receive path.
Darkages.exe:0x00432F10event_manager_queue_event_copyvoid __thiscall(void *event_manager_object, const void *event)Copy one 0x24-byte Event to EventMan work code 0x0F.The consumer path is established, but this build has no native call sites for the wrapper.
Darkages.exe:0x00433110event_process_work_itemvoid __thiscall(void *this, int code, void *data, int value)Convert event-manager work records into input state or Event objects.Work codes 4 through 0x0B handle window input; code 0x0E creates a socket-packet Event and transfers packet ownership.
Darkages.exe:0x00433DC4event_queue_socket_packetvoid __stdcall(uint8_t *packet, uint32_t size)Queue Event type 9 with packet ownership.Called by event_process_work_item; dispatcher work code 3 frees the packet after dispatch.
Darkages.exe:0x00434080event_manager_periodic_noopvoid __thiscall(void *event_manager_object)Perform the native EventMan periodic callback.Empty implementation at vtable slot +0x10; still reached after every EventMan wake or timeout.
Darkages.exe:0x004BE9B0util_thread_queue_ctorvoid *__thiscall(void *worker_object, int queue_capacity)Construct queue state and a suspended worker thread.Cross-subsystem base constructor used by the dispatcher, event manager, Socket, and other worker-derived objects.
Darkages.exe:0x004BEC34util_thread_queue_add_wait_handlevoid __thiscall(void *worker_object, void *wait_handle)Append a Win32 handle to the worker wait set.Constructor uses it to install the work semaphore as wait handle 0; maximum count is 16.
Darkages.exe:0x004BECB0util_thread_queue_dtorvoid __thiscall(void *worker_object)Terminate the worker and close its handles and queue state.Uses TerminateThread; called by derived destructors.
Darkages.exe:0x004BEDF0util_thread_queue_set_wait_timeoutvoid __thiscall(void *worker_object, unsigned int timeout_ms)Set the worker wait timeout.Dispatcher constructor passes 1 millisecond.
Darkages.exe:0x004BEE00util_thread_queue_startvoid __thiscall(void *worker_object)Create the derived wait object and resume the suspended thread.Called by app_initialize for both event workers and by other worker-derived clients.
Darkages.exe:0x004BF250util_thread_queue_worker_loopvoid __thiscall(void *worker_object)Wait for work, signals, or timeout and invoke derived virtual methods.Empty work wakes still call periodic slot +0x10; targets are dispatcher tick, Socket poll, and EventMan no-op.
Darkages.exe:0x00498080util_ring_buffer_push_waitvoid __thiscall(void *ring_buffer, const void *element)Append one fixed-size ring element under the queue monitor.Waits on not-full when the bounded ring has no capacity.
Darkages.exe:0x004981B0util_ring_buffer_pop_waitvoid __thiscall(void *ring_buffer, void *element_out)Remove one fixed-size ring element under the queue monitor.Signals not-full after the copy and count update.
Darkages.exe:0x004BF440util_thread_queue_post_asyncvoid __thiscall(void *worker_object, int code, void *data, int value)Append one asynchronous worker record and wake the worker.Copies the record fields but not pointed-to data; can block for ring capacity even though it does not wait for completion.

File System

The file subsystem covers path construction, lookup order, file access, containers, caching, and the shared readers and writers used by format-specific code.

High-level operation

This section will explain where the client looks for data, how resources are identified, which content is loaded eagerly or on demand, and how file data reaches rendering, audio, user interface, and game systems.

File system behavior has not been mapped yet. Specific layouts will be linked from the Format Catalog.

Code-level flow

This section will trace path construction, open and close operations, archive or container access, allocation, common byte and string readers, bounds handling, caching, decoding, and cleanup. Format-specific flows will link back to their reader and writer functions.

No file system call tree is documented yet.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
No functions documented yet

Networking

The networking subsystem owns the active transport, turns a byte stream into packet bodies, applies the protocol’s XOR transformations, and moves decoded server packets into the pane event hierarchy. Client packet builders use raw byte arrays and the same small big-endian field writers.

The directional action indexes are Client actions and Server actions. Each index links to one payload-relative page per action.

High-level operation

Connection and Windows notification

The normal game transport uses Winsock 1.1 and an IPv4 TCP stream. Connection setup creates an AF_INET, SOCK_STREAM socket, requests a 0x4000 byte Winsock receive buffer, resolves the configured host, and connects to the configured port. Once connected, WSAAsyncSelect associates the socket with the main window using message 0x0402 and event mask 0x21, which is FD_READ | FD_CLOSE.

The main window procedure handles the low word of lParam for message 0x0402:

Winsock eventValueClient action
FD_READ0x0001Queue socket work code 4, which runs the receive framer.
FD_CLOSE0x0020Close the socket and serial handle, clear connection state, and offer the reconnect UI.

The executable also retains serial and printable-text transports. They eventually feed the same logical packet event path, but the standard Stone server path is the binary TCP path described here.

Binary frame

Every binary TCP frame begins with 0xAA. The two-byte size is unsigned and big-endian. It counts the body beginning at the action byte, not the marker or the size itself.

OffsetWidthTypeMeaning
0x001uint8_tMarker, always 0xAA.
0x012uint16_t, big-endianWire body length.
0x03body_lengthbytesAction, transformation metadata where applicable, and payload.

The total frame length is therefore 3 + body_length. The receive code requires a positive body length and stores at most 0x10000 body bytes. Since the wire field is 16 bits, the largest representable wire value is 0xFFFF.

The application-facing logical packet is:

[uint8 action] [payload bytes...] [zero sentinel]

net_c_queue_send receives the action and payload length from a packet builder, copies those bytes, and appends the zero sentinel. The sentinel is an implementation guard, not a checksum. The decoder does not validate its transformed value. After decoding it writes another zero immediately after the returned logical bytes.

Direction defines the action

Action values are not globally unique. A client action and a server action with the same byte can have unrelated meanings. For example, both directions support 0x0D, but the send builders and receive handlers are separate code paths. Protocol implementations must key dispatch by (direction, action), never by action alone.

The executable contains 52 established client action values and 59 server action values. Fifty-one client values occur directly at net_c_queue_send call sites. Action 0x0F is built by the spell-slot path and reaches the queue through the spell-delay controller. Thirty-five values occur in both directions. A server 0x4B packet can also ask the client to forward an embedded logical client packet, so that path is not restricted to one statically embedded client action constant.

Receive and event routing

The TCP reader refills a 0x18000 byte cache with recv and then supplies one byte at a time to the framer. This preserves a partial header or body across Windows notifications and permits multiple complete frames to be drained from one receive cache.

After a frame is complete, the client decodes it when required and posts a copy as internal work code 0x0E. The EventMan worker converts that work item into an Event with type 9, packet pointer at offset +0x14, and packet size at offset +0x18. It queues a copied Event to the dispatcher worker, which recursively walks the pane hierarchy and calls virtual slot +0x40 on each eligible pane. That virtual method is the pane’s server-packet handler.

Dispatch is therefore distributed rather than centralized. MapPane owns the largest switch, while MainMenuPane, ServerSelectDialogPane, exchange panes, bulletin panes, and other UI objects handle actions relevant to their current state. The server action index combines those separate handlers.

Send path

Packet builders assemble a logical packet in a local byte array and call net_c_queue_send. The Socket object copies the bytes, appends the sentinel, and queues work code 5. The socket worker then applies the client transformation rule, allocates body_length + 3 bytes, writes the frame header, and calls Winsock send once.

The send path treats SOCKET_ERROR as failure. It does not retry when send succeeds with a byte count smaller than the requested frame length. A compatible implementation should use a full-write loop even though this client does not.

During a server transfer state, net_c_queue_send drops every client action except 0x10, the client transfer-server action.

Queue concurrency and Socket affinity

The Socket work queue has capacity 0x80. Its ring is protected by a monitor and paired not-full and not-empty conditions. net_c_queue_send can therefore run concurrently on a native client thread and an injected producer without racing the head, tail, count, or copied packet ownership.

The enqueue is not nonblocking. util_thread_queue_post_async calls util_ring_buffer_push_wait, which waits indefinitely when the ring is full, then signals the worker semaphore after admission. Calling it from an IPC read thread would make pipe liveness depend on Socket progress. Calling it from the Socket consumer itself can deadlock if other producers refill the ring before the self-enqueue.

Sequence, XOR state, framing buffers, and the transport call are mutated only by the Socket worker after it consumes code 5. A production Event Proxy should preserve that affinity. The documented design wakes the Socket worker with an empty native-queue signal and drains a bounded proxy queue from the worker’s net_poll_receive periodic callback. A simpler dedicated producer thread may call net_c_queue_send, but it must be drained before EventMan destroys its owned Socket during app_shutdown.

The active Socket is reachable through net_socket_instance at Darkages.exe:0x004F51BC. EventMan owns the Socket at object offset +0x68, publishes the static root during construction, and clears it during destruction. This is the established runtime root for logical packet injection, but callers must still validate the live net_socket_vtable before using a cached pointer. See Event Proxy Hooks and Injection for the injection contract and ownership boundaries.

Packet representation and C++ class evidence

The traced send and receive paths do not instantiate a class per packet or a common ClientPacket or ServerPacket object. Client builders write directly into byte arrays. The receiver copies decoded bytes into an Event, and pane handlers switch on packet[0] and call field readers on later offsets.

The code does establish these surrounding C++ types through constructors, virtual tables, source assertions, and diagnostics:

Established typeEvidence in the packet path
SocketSocket.cpp diagnostics, a constructor-installed virtual table, transport state fields, and virtual work-item processing.
EventSocket Event type 9 owns the packet pointer and size and frees the packet after dispatch.
MainMenuPaneNamed diagnostics and virtual table Darkages.exe:0x005177C0; handles initial version and key negotiation.
MapPaneMapPane.cpp, named method diagnostics, and virtual table Darkages.exe:0x00519280; owns the main in-game action switch.
ServerSelectDialogPaneNamed constructor and deletion diagnostics and virtual table Darkages.exe:0x00526140; handles server action 0x56.

Names such as kServerRequestPortrait and kClientTransferServer are enum-style action names embedded in diagnostics, not evidence of packet classes. The safest compatible-client model for the mapped path is a direction-tagged byte buffer plus per-action serializers and handlers.

Big-endian serialization

The packet builders and handlers share small integer helpers:

WidthWriterReaderByte order
1 bytenet_write_u8net_read_u8Not applicable
2 bytesnet_write_u16_benet_read_u16_beMost significant byte first
4 bytesnet_write_u32_benet_read_u32_beMost significant byte first

The writers place a zero byte immediately after the value. Packet builders subsequently overwrite that byte when adding the next field, or leave it as the packet sentinel at the final logical offset.

Sequence and XOR transformation

Ordinary bodies carry a sequence byte and three XOR passes over the payload and trailing zero. The message indexes label this branch Encrypted: Yes, but the transformation does not provide authentication or cryptographic security.

The default repeating key is the nine ASCII bytes NexonInc., the default table is generated by function 0, and the client send sequence starts at zero. Clear server action 0x00, subtype 0x00, can replace the nine-byte key and select any table function from 0 through 9. Only encrypted client packets increment the client send sequence.

The decoder does not verify that the transformed trailing byte becomes zero. It retains that byte and writes a second local zero guard after the returned logical packet.

See Sequence and XOR Transformation for the exact wire placement, pass order, negotiation payload, all ten table formulas, and implementation addresses.

Failure and bounds behavior

The standard receive path has these established edge behaviors:

  • Outside a frame, the next consumed byte is asserted to be 0xAA; the code does not scan ahead for a later marker.
  • A body length of zero or greater than 0x10000 fails an internal assertion. The two-byte wire field cannot encode a value greater than 0xFFFF.
  • recv == 0 closes the socket, sets it to INVALID_SOCKET, clears connection state, and stops reading.
  • recv == SOCKET_ERROR clears the receive cache and returns no byte. This path does not distinguish individual WSAGetLastError values.
  • Completed packet bytes are copied for the event worker. The Event owns the copy and frees it after pane dispatch.
  • No checksum or message authentication value is validated by the mapped frame or XOR code.

Code-level flow

Startup and connection

  1. Darkages.exe:0x004A32F0 net_socket_ctor installs the Socket virtual table, initializes the transport buffers and state, and installs NexonInc. as the default repeating key.
  2. Darkages.exe:0x004A59F4 net_connect_configured_server starts Winsock 1.1, checks that version 1.1 was returned, resolves the configured host, creates and connects the TCP socket, and registers WM_USER + 2 with FD_READ | FD_CLOSE.
  3. Darkages.exe:0x0045BDC0 win_main_window_proc receives message 0x0402. FD_READ calls net_queue_receive; FD_CLOSE calls net_handle_disconnect.

Receive, decode, and dispatch

win_main_window_proc
  -> net_queue_receive                         work code 4
  -> net_process_work_item
      -> net_s_receive_frames
          -> net_read_transport_byte
              -> recv                          refill up to 0x18000 bytes
          -> net_s_decode_packet_body          ordinary server actions
          -> event_post_socket_bytes           work code 0x0E
              -> event_process_work_item
                  -> event_queue_socket_packet Event type 9
                      -> event_dispatch
                          -> event_dispatch_hierarchy
                              -> pane vtable +0x40

net_s_receive_frames maintains three important pieces of persistent state: whether a frame is open, the number of header or body bytes collected, and the current body length. It combines the first two body-header bytes as (high << 8) | low. Completion occurs after exactly body_length bytes have been placed in the packet buffer.

event_dispatch_hierarchy tests Event type 9 with event_is_socket_packet before calling pane virtual slot +0x40. The return value indicates whether the event was consumed. After event_dispatch returns, event_dispatcher_process_work_item frees the packet pointer for a socket Event and deletes the copied Event.

Build, encode, and send

per-action packet builder
  -> net_write_u8 / net_write_u16_be / net_write_u32_be
  -> net_c_queue_send                          copy and append zero
      -> net_process_work_item                 work code 5
          -> net_c_send_packet_body
              -> net_c_encode_packet_body      ordinary client actions
              -> net_write_u8(0xAA)
              -> net_write_u16_be(body_length)
              -> send                          one call

net_c_encode_packet_body returns one byte more than its input size because it inserts the sequence after the action. net_s_decode_packet_body returns one byte less than its input size because it removes that sequence. Bypass actions keep their original body size.

Representative server dispatch

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet is the largest action switch. It accepts 31 action values and rejects the other values in its 0x03 through 0x4B switch range. Important established branches include:

Server actionCallee or effect
0x03Transfer-server processing.
0x0ASecondary subtype dispatch within MapPane.
0x0EDelete an object from map state.
0x11ui_map_handle_object_direction, which reads a big-endian object ID and a direction from 0 through 3.
0x32Inline tile or object updates.
0x4Bnet_forward_embedded_client_packet, which reads a big-endian embedded length and sends the embedded logical client packet.

The complete accepted set and all other pane handlers are in the server action index. The separate exit-wait pane handles SReconnect, server action 0x4C. Its subtype-one branch completes an orderly-exit exchange and does not call the Socket reconnect path.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
Darkages.exe:0x00431B84event_dispatchint __thiscall(void *this, void *event)Dispatch one internal Event.Calls event_dispatch_hierarchy; participates in capture and consumed-event handling.
Darkages.exe:0x00431D54event_dispatch_hierarchyint __thiscall(void *this, void *event, void *hierarchy)Recursively deliver an Event to panes.Calls pane virtual slot +0x40 for socket Event type 9.
Darkages.exe:0x00432610event_is_socket_packetint __thiscall(void *event)Test for socket Event type 9.Reads the Event type byte at +0x0C.
Darkages.exe:0x00432E50event_post_socket_bytesvoid __thiscall(void *this, const uint8_t *packet, int length)Copy decoded packet bytes to the worker queue.Posts work code 0x0E.
Darkages.exe:0x00433110event_process_work_itemvoid __thiscall(void *this, int code, void *data, int value)Convert worker records to internal Events.Code 0x0E calls event_queue_socket_packet.
Darkages.exe:0x00433DC4event_queue_socket_packetvoid __stdcall(uint8_t *packet, uint32_t size)Create and queue a socket Event.Sets Event type 9, pointer +0x14, and size +0x18.
Darkages.exe:0x0045BDC0win_main_window_procLRESULT __stdcall(HWND, UINT, WPARAM, LPARAM)Main window procedure and Winsock notification bridge.Message 0x0402 handles FD_READ and FD_CLOSE.
Darkages.exe:0x004A32F0net_socket_ctorvoid *__thiscall(void *this, void *event_sink)Construct and initialize the Socket object.Installs default key and transport buffers.
Darkages.exe:0x004A3294net_socket_dtorvoid __thiscall(void *socket_object)Close transport handles and tear down the Socket worker.Called by the deleting destructor owned by EventMan; worker teardown uses TerminateThread.
Darkages.exe:0x004A3560net_queue_receivevoid __thiscall(void *this)Queue receive processing.Posts Socket work code 4.
Darkages.exe:0x004A3570net_c_queue_sendvoid __thiscall(void *this, const uint8_t *packet, int16_t length)Copy and queue a logical client packet.Adds the zero sentinel and posts work code 5.
Darkages.exe:0x004A36C0net_queue_xor_keyvoid __thiscall(void *this, unsigned int key_length, const uint8_t *key)Queue a replacement XOR key.Copies the key and posts Socket work code 10; only called by the clear action 0x00 handler.
Darkages.exe:0x004A3700net_queue_xor_table_functionvoid __thiscall(void *this, unsigned int function_index)Queue an XOR-table function selector.Posts Socket work code 11; only called by the clear action 0x00 handler.
Darkages.exe:0x004A3770net_write_u32_bevoid __cdecl(uint32_t value, uint8_t *dest)Write a big-endian 32-bit field.Writes a zero byte at dest[4].
Darkages.exe:0x004A37D0net_read_u8uint32_t __cdecl(const uint8_t *src)Read an unsigned byte.Used by server action handlers.
Darkages.exe:0x004A3810net_read_u16_beuint32_t __cdecl(const uint8_t *src)Read a big-endian 16-bit field.Used by framing-related and packet handlers.
Darkages.exe:0x004A38B0net_read_u32_beuint32_t __cdecl(const uint8_t *src)Read a big-endian 32-bit field.Returns the combined 32-bit value.
Darkages.exe:0x004A3910net_handle_disconnectvoid __thiscall(void *this)Close transports and handle connection loss.Called for FD_CLOSE.
Darkages.exe:0x004A39C0net_poll_receivevoid __thiscall(void *this)Poll transport readiness outside the async window path.Uses select for TCP and calls net_s_receive_frames.
Darkages.exe:0x004A3A74net_s_receive_framesvoid __thiscall(void *this)Extract, decode, and post complete packets.Calls net_read_transport_byte, net_s_decode_packet_body, and event_post_socket_bytes.
Darkages.exe:0x004A44D4net_s_decode_packet_bodyint __stdcall(const uint8_t *src, int src_size, uint8_t *dest)Reverse the ordinary server-body XOR transformation.Returns src_size - 1.
Darkages.exe:0x004A4D34net_read_transport_byteint __thiscall(void *this, uint8_t *out_byte)Supply one cached transport byte.Refills with recv or ReadFile.
Darkages.exe:0x004A54D0net_process_work_itemvoid __thiscall(void *this, int code, void *data, int value)Execute queued Socket work.Codes 4, 5, 10, and 11 drive receive, send, key, and table operations.
Darkages.exe:0x004A59F4net_connect_configured_servervoid __thiscall(void *this)Establish the configured connection.Uses Winsock startup, resolution, connect, and WSAAsyncSelect.
Darkages.exe:0x004A72B4net_c_send_packet_bodyvoid __thiscall(void *this, const uint8_t *packet, int16_t length)Transform, frame, and send a client packet.Calls send once and frees the temporary frame.
Darkages.exe:0x004A7940net_write_u8void __cdecl(uint32_t value, uint8_t *dest)Write one byte.Writes a zero byte at dest[1].
Darkages.exe:0x004A7990net_write_u16_bevoid __cdecl(uint32_t value, uint8_t *dest)Write a big-endian 16-bit field.Used for the frame length and packet fields.
Darkages.exe:0x004A79E4net_c_encode_packet_bodyint __stdcall(const uint8_t *src, int src_size, uint8_t *dest)Apply the ordinary client-body sequence and XOR transformation.Returns src_size + 1.
Darkages.exe:0x00498080util_ring_buffer_push_waitvoid __thiscall(void *ring_buffer, const void *element)Append a synchronized bounded-ring record.Waits indefinitely on the not-full condition when the Socket queue reaches 128 records.
Darkages.exe:0x004A8414net_set_xor_keyvoid __stdcall(int length, const uint8_t *key)Replace the runtime XOR key.Requires length at most 9 and writes four repeated copies.
Darkages.exe:0x004A8590net_build_xor_tablevoid __stdcall(int function_index)Generate the 256-entry XOR table.Accepts function indexes 0 through 9.
Darkages.exe:0x00468A90ui_map_dispatch_server_packetint __thiscall(void *this, void *event)Dispatch in-game server actions in MapPane.Supports 31 fixed action values.
Darkages.exe:0x0046B574ui_map_handle_object_directionint __thiscall(void *this, const uint8_t *packet)Apply a server direction update to a map object.Called by the MapPane action 0x11 branch; reads a big-endian object ID at +0x01 and direction at +0x05.
Darkages.exe:0x0046CA54net_forward_embedded_client_packetestablished MapPane action handlerForward the client packet embedded in server action 0x4B.Reads a big-endian length and calls net_c_queue_send.
Darkages.exe:0x0045F780ui_main_menu_handle_server_packetint __thiscall(void *this, void *event)Handle main-menu server actions.Supports 0x00, 0x02, 0x03, and 0x0A.
Darkages.exe:0x004921F0ui_exit_wait_handle_server_packetint __thiscall(void *this, void *event)Handle server action 0x4C in the exit-wait pane.Subtype 0x01 sends CQuit subtype 0x00 and displays the safe-exit text; no reconnect call is made.
Darkages.exe:0x00492310ui_exit_wait_pane_ctorestablished __thiscall constructorCreate the exit-wait pane.Displays the wait warning and sends CQuit subtype 0x01.
Darkages.exe:0x004A2ED0ui_server_select_handle_server_packetint __thiscall(void *this, void *event)Deserialize the server table from action 0x56.Owns and replaces the per-server entry allocation.
Darkages.exe:0x0040B3A0ui_handle_server_request_portraitint __thiscall(void *this, void *event)Handle kServerRequestPortrait, action 0x49.Calls net_c_build_portrait_response.
Darkages.exe:0x0040A664net_c_build_portrait_responseestablished __thiscall builderBuild client action 0x4F from a local portrait.Called after server action 0x49.
Darkages.exe:0x004C33B2recvWinsock importRefill the TCP receive cache.Called with a maximum length of 0x18000.
Darkages.exe:0x004C33D6sendWinsock importSend a complete allocated frame.The caller does not handle a short successful write.
Darkages.exe:0x004C33D0WSAAsyncSelectWinsock importRegister socket notifications with the main window.Uses message 0x0402 and mask 0x21.

Event Proxy Architecture

An Event Proxy can run inside the client and expose logical input, internal Events, decoded server packets, logical client packets, and selected runtime state to an external controller. It can provide most of the useful behavior of a network proxy without redirecting the TCP connection and without asking the controller to reproduce sequence, XOR, framing, or socket state.

This section is a development design based on confirmed Dark Ages 4.21 client boundaries. The client functions, object roots, ownership rules, and worker paths are established by static analysis. The injected DLL, IPC protocol, rule engine, and version resolver described here are proposed components and have not yet been implemented or validated by a controlled runtime experiment.

High-level operation

Design goals

The injected module should satisfy these requirements:

RequirementDesign response
Observe all logical trafficHook decoded server packet posting, central internal Event dispatch, and logical client packet queueing.
Inject client-recognized eventsAdmit commands into bounded proxy queues and execute the established packet, input, or pane action from its owning client worker.
Send any logical client packetQueue an action and payload for the Socket worker pump; it appends the sentinel and enters net_c_send_packet_body, which adds sequence, XOR transformation, frame, and transport send.
Block without an IPC round tripEvaluate an immutable local ruleset in the hook and make the pass or block decision before telemetry is sent.
Continue when no controller existsThe initial and fallback policy is pass-through. Hook execution never waits for the pipe.
Survive controller exit and reconnectThe DLL owns a persistent named-pipe accept loop. A controller connection is a replaceable session, not the lifetime of the injected module.
Supply state after late attachProduce a stable typed snapshot from static roots and active pane trees, then replay events newer than the snapshot boundary.

“All events” means every event category the mapped client recognizes. Mouse Event types are 0 through 7, keyboard is type 8, and decoded server packets are type 9. Pane timers are a separate callback path and are not Event objects. Unknown Event type values reach an assertion path instead of a generic extension handler, so the public IPC API should reject unknown types.

Component layout

flowchart LR
    Controller["External controller"] <-->|"duplex named pipe"| IPC["IPC service thread"]

    subgraph Proxy["Injected Event Proxy DLL"]
        IPC --> Commands["Bounded proxy command queues"]
        Hooks["Hook adapters"] --> Rules["Local immutable rules"]
        Hooks --> Telemetry["Bounded telemetry ring"]
        Telemetry --> IPC
        Commands --> SocketPump["Socket worker pump"]
        Commands --> EventPump["EventMan worker pump"]
        Commands --> DispatchPump["Dispatcher worker pump"]
        Snapshot["Typed state snapshotter"] --> IPC
    end

    Win32["Win32 messages"] --> EventMan["EventMan worker"]
    Receive["Socket receive and decode"] --> Inbound["event_post_socket_bytes"]
    Inbound --> EventMan
    EventMan --> Dispatch["event_dispatch"]
    Dispatch --> Panes["Pane hierarchy"]
    Builders["Client packet builders"] --> Outbound["net_c_queue_send"]
    Outbound --> SocketWorker["Socket worker: sequence, XOR, frame, send"]

    Hooks -. "observe or block" .-> Inbound
    Hooks -. "observe or block" .-> Dispatch
    Hooks -. "observe or block" .-> Outbound
    SocketPump -. "worker-affine send" .-> SocketWorker
    EventPump -. "worker-affine inject" .-> EventMan
    DispatchPump -. "UI and snapshot commands" .-> Dispatch
    Snapshot -. "read validated roots" .-> Panes

The hooks remain synchronous only for the local decision. Telemetry is copied into a bounded ring and delivered later by the IPC thread. Commands travel in the other direction through bounded proxy-owned queues. The target client worker drains each queue so IPC code does not call a live client object from an unrelated thread.

Threading model and pump placement

The named-pipe pump should run on a proxy-owned thread, but that thread should stop at framing, validation, ruleset compilation, command admission, and response serialization. It should not call net_c_queue_send, event_post_socket_bytes, pane methods, or timer methods.

The strongest 4.21 design has one bounded command queue for each client ownership domain:

QueueExecution contextWake and pump pointAllowed work
Socket commandsSocket workerSignal wait handle 0 at Socket +0x14; drain from the net_socket_vtable +0x10 callback around net_poll_receiveLogical client packet sends and Socket-owned queries.
EventMan commandsEventMan workerSignal wait handle 0 at EventMan +0x14; replace or wrap event_manager_vtable +0x10, whose native target is event_manager_periodic_noopDecoded server packet and normalized input injection.
Dispatcher commandsDispatcher workerSignal wait handle 0 at dispatcher +0x14; drain from a bounded event_dispatcher_tick hookPane methods, timer operations, snapshot barriers, and validated UI writes.

This wake mechanism is supported by the generic worker loop. A wait-handle-0 wake first checks whether the native ring is empty. If it is empty, the loop skips the native pop and still invokes virtual slot +0x10. A proxy can therefore enqueue into its own ring, release the existing work semaphore once, and let the correct client worker enter the class-specific pump without adding a fake native work record.

Command admission and the matching ReleaseSemaphore call must run inside a proxy lifecycle read guard. Admission rechecks the active state after acquiring the guard, validates the live root, vtable, and wait handle, copies the command, and checks the wake result before leaving. The shutdown hook changes the state to draining and waits for these guards before the original client can close any worker handle. This closes the race where an IPC thread captures Socket or EventMan +0x14 immediately before destruction.

Each pump should call the original periodic method and then process a small fixed number of commands. The Socket and EventMan pumps can normally process one packet per wake. The dispatcher pump needs both an item limit and a short elapsed-time budget because its thread runs at THREAD_PRIORITY_TIME_CRITICAL and also owns deferred deletion and timers. None of the pumps should wait for pipe I/O or a controller response.

The client native queues are synchronized, so a simpler first implementation can use a separate proxy command-executor thread that calls net_c_queue_send and event_post_socket_bytes. This does not race the native ring indices. It is not the preferred final design because both wrappers can wait indefinitely when their 128-record queues are full, and a blocked foreign producer complicates Socket and EventMan destruction. Never make those calls on the named-pipe accept or read thread.

Hook layers

Three primary hooks cover different semantics:

  1. event_post_socket_bytes sees one decoded server packet before Event creation. It is the cleanest inbound packet filter because skipping the original call prevents allocation and pane delivery.
  2. event_dispatch sees one logical Event before hierarchy fan-out. It covers input and packets at a common point and preserves the dispatcher’s normal cleanup when a hook reports the Event consumed.
  3. net_c_queue_send sees one native logical client packet before the sequence byte, XOR transformation, frame header, and Winsock call. It is the outbound observation point and a synchronized fallback injection adapter. The preferred controller injection path runs on the Socket worker and enters net_c_send_packet_body directly.

Hooking event_dispatch_hierarchy alone is not a replacement for the central Event hook. It is recursive, visits children before parents, translates mouse coordinates during traversal, and may be called more than once for one Event. It is useful for pane-specific diagnostics, but it produces duplicate observations and is a poor global blocking boundary.

Injection lifecycle

The DLL entry point should do only loader-safe work. Windows holds the loader lock while it calls DllMain, so starting IPC, resolving imports, installing hooks, or waiting for client initialization there can deadlock with other module loads. A small entry point can disable thread notifications and arrange for initialization after LoadLibrary returns. Injector-assisted invocation of an exported proxy_start function is the cleanest option. A separately created bootstrap thread is a practical fallback.

The proxy lifecycle should be explicit:

Proxy stateAllowed behavior
loadedRecord module handle and reject IPC work.
resolvingFingerprint the image, resolve a known profile, and validate every required symbol.
passiveRun IPC and state reads, but do not install mutation-capable hooks when validation is incomplete.
activeHooks, local rules, telemetry, packet injection, and typed snapshots are available.
drainingReject new mutations, use pass-through rules, stop command execution, and remove hooks.
detachedClose proxy-owned handles and leave no client callback pointing into the DLL.

Early injection does not require guessing how long initialization takes. The bootstrap can validate the image immediately, then wait until event_dispatcher, event_manager_instance, and net_socket_instance are nonnull and have their expected vtables. Late injection follows the same validation and can enter active as soon as those roots are stable.

app_shutdown is the critical detach boundary. It destroys EventMan, whose destructor destroys its owned Socket and clears net_socket_instance, before the final dispatcher destruction. The proxy should intercept shutdown entry, enter draining, stop accepting mutating commands, atomically select a pass-through ruleset, wait for in-flight hook calls to leave, and remove hooks in reverse order. It must not depend on a client worker shutdown notification because the shared worker destructor uses TerminateThread rather than a cooperative stop and join.

Failure isolation and pass-through

The injected module is part of the game process, so its failure policy must favor the unmodified client path:

  • A missing or unknown build profile disables hooks that can mutate state.
  • No controller connection means pass-through unless an explicitly persistent local ruleset is installed.
  • A full telemetry ring drops telemetry, increments a loss counter, and continues the client call.
  • A malformed IPC message is rejected before it reaches a client function.
  • A controller timeout never blocks a client worker.
  • A hook reentry guard prevents a rule-generated packet from recursively generating the same rule action without a limit.
  • Shutdown invalidates all cached client pointers before closing the pipe.

Session rules should be removed automatically when their controller disconnects. Persistent rules should require an explicit flag and remain local to the DLL across reconnects. This distinction preserves seamless default pass-through while still allowing a block rule to operate without live IPC.

Code-level flow

Activation path

  1. Fingerprint the loaded Darkages.exe image and select a version profile.
  2. Resolve every required RVA to loaded_module_base + rva.
  3. Decode and validate the expected instructions, call relationships, globals, and vtables before changing code.
  4. Start the pipe accept loop and publish passive build and capability information.
  5. Wait for event_dispatcher, event_manager_instance, and net_socket_instance to be live.
  6. Install the inbound, Event, and outbound hooks. Install the Socket and EventMan worker pumps for packet mutation capabilities, and the dispatcher pump for pane, timer, write, or snapshot capabilities.
  7. Atomically change the capability state to active.

Hook installation should use one transaction where the hooking library supports it. If any required hook fails, roll back the complete set and remain passive. A partial set can create misleading ordering, such as allowing injected packets without observing the resulting outbound queue.

Normal packet and Event flow

decoded server packet
  -> inbound local rules
      -> block: return without event posting
      -> pass or copy-on-write modify
          -> event_post_socket_bytes
              -> EventMan work code 0x0E
                  -> Event type 9
                      -> central Event rules
                          -> block: report consumed; dispatcher still frees payload
                          -> pass: event_dispatch hierarchy fan-out

native client builder
  -> outbound local rules
      -> block: return without queue allocation
      -> pass or copy-on-write modify
          -> net_c_queue_send
              -> Socket work code 5
                  -> sequence, XOR, frame, send

proxy client command
  -> bounded Socket command queue and worker wake
      -> Socket periodic pump
          -> outbound local rules and telemetry
              -> net_c_send_packet_body
                  -> sequence, XOR, frame, send

The inbound and Event hooks can both observe a server packet. Telemetry therefore includes a phase field. A default subscription should publish decoded ingress at the first hook and suppress the duplicate type 9 record at the central Event hook unless the controller explicitly requests both phases.

Worker-affine packet pumps

For a client packet, the Socket pump validates the live root and vtable, checks the same transfer gate used by net_c_queue_send, applies outbound rules, appends the zero sentinel in proxy-owned storage, and calls net_c_send_packet_body with logical_length + 1. Static analysis shows that this function reads the input synchronously, applies the normal sequence, XOR, framing, and send path, and neither retains nor frees the input pointer. The proxy pump must emit the controller or local_rule telemetry itself because this route intentionally does not re-enter the net_c_queue_send hook.

For a server packet, the EventMan pump allocates logical_length + 1 through util_memory_manager_alloc, copies the logical bytes, appends zero, and calls event_process_work_item(event_manager, 0x0E, owned_packet, logical_length) on the EventMan worker. Code 0x0E transfers the packet into Event type 9 and the dispatcher later releases it through util_memory_manager_free. A DLL-heap pointer must not be passed into this ownership path.

Input injection belongs on the same EventMan pump. It can invoke the established worker-side work-code behavior with the same normalized scan code, message time, coordinates, or wheel delta that the native wrappers would have queued. This avoids self-enqueueing into EventMan’s bounded ring from its own consumer thread.

Direct pane operations and timer-list operations remain dispatcher-only. event_dispatcher_insert_timer edits the timer list without a visible lock. A timer injection command must run through the dispatcher pump and should target only an established receiver and callback identifier. All commands carry a connection generation and cancellation token so a disconnected controller cannot leave stale work queued.

The Socket worker processes at most one native record and then calls its periodic method. A proxy send performed there is therefore serialized with sequence and transport state, but it is not in a single chronological FIFO with the remaining native Socket queue. If strict native-versus-controller admission order is required, use net_c_queue_send from one dedicated executor and accept its blocking full-queue behavior, or implement a separately validated unified admission hook.

Shutdown path

  1. Enter draining at app_shutdown entry, before the original function deletes EventMan and its owned Socket.
  2. Reject new mutations, stop worker wake signals, cancel queued commands, and atomically select pass-through rules.
  3. Disconnect the current controller and stop the accept loop. Use overlapped pipe I/O or explicit cancellation so this step cannot wait indefinitely on a blocked pipe read or connect.
  4. Wait for IPC admission and wake guards, the Socket, EventMan, and dispatcher pumps, and ordinary hook active-call counters to reach zero while all three client workers are still alive.
  5. Restore worker vtable slots and remove the dispatcher, outbound, Event, and inbound hooks in reverse order.
  6. Clear cached roots and call the original app_shutdown. The EventMan destructor then deletes the Socket, clears both roots, and tears down their workers.
  7. Close proxy-owned handles and mark the module detached.

The proxy should normally remain loaded until process exit. Calling FreeLibrary while another client worker might still execute a trampoline or callback into the module is more dangerous than retaining a small inert DLL.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
Darkages.exe:0x004315B0event_dispatcher_tickvoid __thiscall(void *event_dispatcher_object)Run deferred deletion and one due timer.Optional bounded proxy command-pump location because it runs on the dispatcher worker.
Darkages.exe:0x00431B84event_dispatchint __thiscall(void *event_dispatcher_object, void *event)Dispatch one logical Event.Primary central Event observation and block boundary before hierarchy fan-out.
Darkages.exe:0x00431D54event_dispatch_hierarchyint __thiscall(void *event_dispatcher_object, void *event, void *hierarchy)Recursively deliver an Event to panes.Useful for targeted diagnostics, not for one-record-per-Event telemetry.
Darkages.exe:0x00432E50event_post_socket_bytesvoid __thiscall(void *event_manager_object, const uint8_t *packet, int length)Copy a decoded server packet and post work code 0x0E.Primary decoded inbound packet hook and synchronized fallback injection adapter.
Darkages.exe:0x00432F10event_manager_queue_event_copyvoid __thiscall(void *event_manager_object, const void *event)Copy a 36-byte Event and post EventMan work code 0x0F.No native call sites are present in this build; usable only with a validated Event layout.
Darkages.exe:0x00434080event_manager_periodic_noopvoid __thiscall(void *event_manager_object)Native no-op EventMan periodic callback.event_manager_vtable +0x10; practical vtable-slot pump for EventMan-affine commands.
Darkages.exe:0x0045B8F0app_shutdownvoid __cdecl(void)Tear down application subsystems.Proxy draining and hook-removal boundary.
Darkages.exe:0x0045CCA0app_initializevoid __cdecl(void)Construct the dispatcher, EventMan, Socket, screen, and other subsystems.Early-injection readiness boundary.
Darkages.exe:0x004A3570net_c_queue_sendvoid __thiscall(void *socket_object, const uint8_t *packet, int16_t length)Copy and queue one logical client packet.Primary native outbound hook and synchronized native-queue fallback adapter; admission can block.
Darkages.exe:0x004A39C0net_poll_receivevoid __thiscall(void *socket_object)Poll receive state from the Socket periodic worker callback.net_socket_vtable +0x10; recommended bounded Socket command-pump hook.
Darkages.exe:0x004A72B4net_c_send_packet_bodyvoid __thiscall(void *socket_object, const uint8_t *packet, int16_t length)Apply client transform and framing, then call send.Socket-worker-affine packet adapter; input is read synchronously and is not retained or freed.
Darkages.exe:0x004BF440util_thread_queue_post_asyncvoid __thiscall(void *worker_object, int code, void *data, int value)Append an asynchronous worker record and release the queue semaphore.Does not copy data or wait for completion, but can block while the bounded ring is full.

Event Proxy Hooks and Injection

The 4.21 client exposes logical boundaries on both sides of the internal Event system. Decoded server packets can be intercepted before Event creation, all pane-bound Events can be intercepted once before recursive delivery, and client packets can be intercepted before transport transformation. These are internal functions, so an injected module needs x86 inline detours or equivalent call-site instrumentation rather than Winsock import hooks.

This page distinguishes confirmed client behavior from recommended proxy behavior. Hook return values, copied-buffer ownership, queue codes, Event fields, and static roots are confirmed. Rule evaluation, telemetry, and IPC commands are proposed.

High-level operation

Primary hook matrix

PhaseFunctionData visible to the hookRecommended use
Decoded ingressevent_post_socket_bytes[server action][payload], length excludes the local zero sentinelObserve, block, or copy-on-write modify server packets before Event creation.
Central Eventevent_dispatchOne 36-byte Event before capture-aware hierarchy routingObserve or block mouse, keyboard, and socket Events once per logical Event.
Pane traversalevent_dispatch_hierarchyEvent plus the current packed hierarchy listOptional targeted diagnostics where child and parent delivery order matters.
Logical egressnet_c_queue_send[client action][payload], length excludes the sentinel and all wire metadataObserve, block, or modify native client packets; optional native-queue injection adapter.
Socket pumpnet_poll_receiveSocket worker context after a native wakeBounded worker-affine client packet injection.
EventMan pumpevent_manager_periodic_noop vtable slotEventMan worker context after a native wakeBounded server packet and normalized input injection.
Dispatcher tickevent_dispatcher_tickDispatcher worker context before or after timer processingBounded command pump for direct pane, timer, write, and snapshot operations.

The first and fourth hooks avoid the protocol transformation entirely. Inbound data has already been decoded. Outbound data has not yet received its sequence byte, XOR passes, frame marker, or big-endian frame length.

Packet blocking and mutation

Blocking is simplest at the copied-input boundaries:

  • An inbound block skips event_post_socket_bytes. The receive framer has already consumed the frame, and no EventMan-owned copy exists yet.
  • An outbound block skips net_c_queue_send. The producer retains its stack or object buffer, and the Socket queue has not allocated a copy.
  • A central Event block skips the original event_dispatch, returns 1 as the consumed result, and does not free the Event or packet. event_dispatcher_process_work_item still performs normal socket-payload cleanup and deletes the queued Event after the hook returns.

Packet mutation should be copy-on-write. The proxy copies the logical packet into proxy-owned scratch storage, applies validated edits, and passes that copy to the original client function. Both packet-boundary functions make their own owned copy before returning, so the proxy scratch buffer can be released immediately afterward.

Do not replace Event +0x14 with memory allocated by the DLL’s C runtime. The dispatcher releases an owned socket packet through the client’s memory manager. Inbound packet replacement belongs at event_post_socket_bytes, where the client performs the allocation itself.

Client packet injection

A controller command for an arbitrary logical client packet should contain exactly:

[uint8 action] [payload bytes...]

It must not contain 0xAA, the two-byte frame length, a sequence byte, transformed bytes, or the trailing zero sentinel.

The recommended stable path queues the command in a bounded proxy-owned Socket queue, signals the Socket worker’s existing wait handle 0, and executes it from the Socket periodic callback. The worker-affine adapter checks the transfer gate, appends the sentinel, and calls:

net_c_send_packet_body(
    net_socket_instance,
    packet_with_sentinel,
    logical_length + 1
);

This is the same Socket-worker function reached by native work code 5. It updates sequence and transformation state and performs normal framing and transport output. It reads the supplied packet synchronously and does not retain or free it, so the pump can release its proxy-owned command after the call.

The native-queue alternative calls net_c_queue_send(net_socket_instance, logical_packet, logical_length) from a dedicated proxy executor. The confirmed wrapper contract has several consequences:

  • logical_length includes the action byte and must be positive.
  • The parameter is signed 16-bit. The function adds one for the sentinel before placing the value in another signed 16-bit work field, so the practical maximum input is 32,766 bytes.
  • The function copies the packet and appends the sentinel before it returns.
  • Socket work code 5 performs the later transformation and send.
  • When Socket transfer_gate at +0x780D2 is 1, the function drops every action except 0x10.
  • Its 128-record ring is protected by a monitor and not-full condition, so simultaneous native and proxy producers do not corrupt queue state.
  • The push waits indefinitely when the ring is full. The wrapper is asynchronous with respect to completion, not nonblocking with respect to admission.
  • The function returns no queue or transport success value. An IPC acknowledgement can mean “validated and admitted,” not “sent on TCP.” A later Socket-worker or send observation would be required for delivery-attempt telemetry.

Before either adapter runs, validate that net_socket_instance is nonnull and its first pointer is net_socket_vtable. A useful command response distinguishes not_ready, not_connected, blocked_by_rule, blocked_by_transfer_gate, admitted, executed, and invalid_length.

Server packet injection

To make a logical server packet enter the client as though it had just been decoded, queue the command for the EventMan worker. The pump allocates logical_length + 1 through the client’s util_memory_manager_alloc, copies the packet, appends zero, and invokes the established worker case:

void *memory_manager;
uint8_t *client_owned_packet;

memory_manager = util_memory_manager_get_instance();
client_owned_packet = util_memory_manager_alloc(
    memory_manager,
    logical_length + 1
);
memcpy(client_owned_packet, logical_packet, logical_length);
client_owned_packet[logical_length] = 0;

event_process_work_item(
    event_manager_instance,
    0x0e,
    client_owned_packet,
    logical_length
);

The input begins with the server action and excludes the sentinel. The pump must validate a live MemoryMan root before allocation and free the new buffer itself if command execution stops before work code 0x0E accepts it. Once called, work code 0x0E creates Event type 9, routes it through event_dispatch, and transfers the packet to dispatcher cleanup after pane delivery. Allocation through the executable is required because the dispatcher later calls util_memory_manager_free.

The native-queue alternative calls event_post_socket_bytes(event_manager_instance, logical_packet, logical_length) from a dedicated proxy executor. That wrapper performs the same client allocation and posts EventMan code 0x0E. Its 128-record ring is synchronized for multiple producers but can block for capacity, so it must not run on the named-pipe thread or on the EventMan consumer itself.

This path preserves normal pane filtering. A packet is acted on only if an active registered pane claims its action. For example, injecting an in-game action while only MainMenuPane is registered does not bypass the UI state machine.

Input injection

Two input modes are useful:

ModeMechanismSemantics
Window-faithful inputPostMessageA(app_main_window, ...)Runs the original window procedure, including software-cursor updates, scan-code extraction, EventMan input blocking, and message-specific quirks.
Internal inputCall event_queue_mouse_* or event_queue_key_* on event_manager_instanceEnters the existing EventMan queue directly but does not perform the window procedure’s cursor drawing and coordinate preprocessing.

The complete handled WM_*, WM_USER + 2 Winsock notification, and application-defined WM_USER + 0x2046 behavior is indexed in Input and Windows Events. Window-faithful injection should be limited to messages whose parameter layout is established there.

For an internal mouse click at a new position, queue a mouse move first, then the button transition. Button work uses the current EventMan mouse coordinates. Queue order preserves the move-before-click relationship. Releases should normally be paired even when a press was blocked because the client deliberately allows button-up and key-up work while input blocking is active.

For the worker-affine architecture, the proxy command queue preserves the same move-before-click ordering and the EventMan pump invokes the corresponding event_process_work_item cases directly. Calling an input queue wrapper from the EventMan consumer can self-block if concurrent producers refill the native ring before the wrapper pushes.

Direct keyboard injection uses the client’s physical scan-code representation. Extended keys have 0x80 added to the base scan byte. Passing a virtual-key code instead produces the wrong mapping. The controller should either supply the normalized scan byte or request a window-faithful WM_KEYDOWN or WM_KEYUP with a correctly formed lParam.

Thread-affinity matrix

OperationRequired execution contextReason
Pipe accept, read, write, handshakeProxy IPC threadMay block on IPC and must not own client state.
Rule compilation and subscription changesProxy IPC threadPublishes immutable state atomically; no client call is needed.
Client packet injectionSocket worker pumpSerializes sequence, XOR, framing, and transport state.
Server packet and normalized input injectionEventMan worker pumpPreserves input state and packet ownership before dispatcher handoff.
Pane method, timer, typed UI write, snapshot barrierDispatcher worker pumpThese structures have no established external-thread lock.
Hook telemetry publicationCalling client threadBounded copy only; never writes the pipe or waits.

The proxy can keep a dedicated executor that calls native queue wrappers as a simpler implementation, but it is an admission adapter rather than the IPC pump. Use one executor, one in-flight client call, bounded proxy input, and an app_shutdown drain gate. The worker-affine pumps remove that foreign-thread lifetime edge and are the recommended production layout.

Generic Event injection

The Event object is 36 bytes and uses event_vtable. The mapped variants share these fields:

OffsetWidthEvent contextMeaning
+0x004Allevent_vtable pointer.
+0x044Allunknown_04 base-object marker initialized by the common constructor.
+0x084Allunknown_08.
+0x0C1AllType: mouse subtype 0 through 7, keyboard 8, socket packet 9.
+0x104MouseX coordinate.
+0x144MouseY coordinate.
+0x181MouseModifier and button state byte.
+0x1C4WheelWheel steps after signed delta division by 120.
+0x204MouseMessage timestamp.
+0x101KeyboardMapped internal key value.
+0x111KeyboardActive modifier byte.
+0x144KeyboardMessage timestamp.
+0x144SocketOwned decoded-packet pointer.
+0x184SocketPositive decoded-packet size.

event_manager_queue_event_copy allocates and shallow-copies all 36 bytes, then posts EventMan work code 0x0F. The EventMan worker forwards another copy to the dispatcher and deletes its first copy. This function has no native call sites in the analyzed build, although its implementation and worker case are complete.

Use this generic path only for a fully mapped non-owning Event layout. Native input queue functions are safer for mouse and keyboard because they also update EventMan state. event_post_socket_bytes is safer for socket packets because it establishes correct packet allocation and ownership. An IPC INJECT_EVENT_RAW command should therefore be disabled by default and restricted to a version profile with explicit field validation.

Timers and pane-specific actions

Pane timers do not become Events. event_dispatcher_tick removes a timer record and calls receiver vtable slot +0x44 with a callback identifier and two payload values. event_dispatcher_insert_timer edits the dispatcher’s timer list directly and has no visible lock.

A proxy timer command must execute on the dispatcher worker, validate that the receiver is still registered, and use only a callback contract established for that pane class. An arbitrary receiver, callback identifier, or payload can violate pane state assumptions. Packet-driven UI should normally be exercised by injecting its server packet, and user-driven UI should normally be exercised by input injection, rather than calling class handlers directly.

Code-level flow

Inbound hook flow

hook_event_post_socket_bytes(event_manager, packet, length)
  -> validate length and read action packet[0]
  -> evaluate local ingress rules
  -> enqueue telemetry copy without waiting
  -> block: return
  -> modify: call original with scratch packet
  -> pass: call original with original packet
      -> copy length + 1
      -> append zero
      -> util_thread_queue_post_async(code 0x0E)

The hook must not retain the caller’s packet pointer. Normal TCP receive uses a persistent Socket decode buffer that is reused for later frames.

Central Event hook flow

event_dispatcher_process_work_item(code 3, event)
  -> hook_event_dispatch(dispatcher, event)
      -> classify event[0x0C]
      -> evaluate local Event rules
      -> enqueue bounded telemetry
      -> block: return consumed
      -> pass: call original event_dispatch
  -> if socket Event, free event[0x14]
  -> delete Event

Because cleanup belongs to the caller, the hook must never delete the Event. A telemetry record must copy all needed scalar fields and packet bytes before returning.

Outbound hook flow

native client packet builder
  -> hook_net_c_queue_send(socket, packet, length)
      -> validate action and signed length
      -> evaluate local egress rules
      -> enqueue telemetry copy without waiting
      -> block: return
      -> modify: call original with scratch packet
      -> pass: call original with original packet
          -> enforce transfer gate
          -> allocate and copy length + 1
          -> append zero
          -> util_thread_queue_post_async(code 5)
              -> net_process_work_item
                  -> net_c_send_packet_body

The hook should tag origin as client, controller, or local_rule. A thread-local reentry counter and a maximum rule-action depth prevent an injected or rule-generated packet from triggering an unbounded emission cycle. The packet should still be observable unless a subscription explicitly excludes non-client origins.

Worker-affine injected packets do not pass through net_c_queue_send. The Socket pump runs the same immutable outbound rules, publishes a logical-egress record with the command origin, appends the sentinel, and calls net_c_send_packet_body. This avoids recursive hook entry while retaining one telemetry model.

proxy Socket command
  -> wake Socket wait handle 0
  -> worker calls original net_poll_receive
  -> Socket pump validates root, vtable, generation, and transfer gate
  -> evaluate outbound rules and publish telemetry
  -> net_c_send_packet_body(packet_with_sentinel, logical_length + 1)

Detour ABI requirements

These functions are x86 __thiscall. The object pointer arrives in ECX, while the remaining arguments are on the stack. A detour wrapper must preserve the nonvolatile registers, stack cleanup convention, and original ECX. The trampoline must relocate complete overwritten instructions and any relative branches or calls.

Before patching a target, compare its bytes with the selected build profile and decode enough whole instructions for the jump stub. Do not install a detour from a raw address match alone. The Version Profiles page defines the required semantic validation.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
Darkages.exe:0x00431200event_dispatcher_queue_event_copyvoid __thiscall(void *event_dispatcher_object, const void *event)Allocate and shallow-copy a 36-byte Event, then post dispatcher work code 3.Native mouse, key, and socket Event producers use this queue.
Darkages.exe:0x004315B0event_dispatcher_tickvoid __thiscall(void *event_dispatcher_object)Run periodic dispatcher work.Optional bounded dispatcher-thread proxy command pump.
Darkages.exe:0x004316E0event_dispatcher_process_work_itemvoid __thiscall(void *event_dispatcher_object, int code, void *data, int value)Process dispatcher queue records.Code 3 dispatches, frees an owned socket payload, and deletes the Event.
Darkages.exe:0x00431B84event_dispatchint __thiscall(void *event_dispatcher_object, void *event)Apply capture handling and dispatch one Event.Primary one-record-per-Event hook.
Darkages.exe:0x00431D54event_dispatch_hierarchyint __thiscall(void *event_dispatcher_object, void *event, void *hierarchy)Recursively call pane input or socket handlers.Child lists are visited before their current pane.
Darkages.exe:0x00432BD0event_queue_mouse_movevoid __thiscall(void *event_manager_object, int mouse_y, int mouse_x, uint32_t message_time)Queue internal mouse movement.Work code 4; updates EventMan coordinates when processed.
Darkages.exe:0x00432C50event_queue_left_button_downvoid __thiscall(void *event_manager_object, uint32_t message_time)Queue left-button press.Work code 5; uses current EventMan coordinates.
Darkages.exe:0x00432C90event_queue_left_button_upvoid __thiscall(void *event_manager_object, uint32_t message_time)Queue left-button release.Work code 6; not suppressed by input blocking.
Darkages.exe:0x00432CC0event_queue_right_button_downvoid __thiscall(void *event_manager_object, uint32_t message_time)Queue right-button press.Work code 7.
Darkages.exe:0x00432D00event_queue_right_button_upvoid __thiscall(void *event_manager_object, uint32_t message_time)Queue right-button release.Work code 8; not suppressed by input blocking.
Darkages.exe:0x00432D30event_queue_mouse_wheelvoid __thiscall(void *event_manager_object, int wheel_delta, uint32_t message_time)Queue signed wheel input.Work code 9; worker divides by 120.
Darkages.exe:0x00432D60event_queue_key_downvoid __thiscall(void *event_manager_object, uint8_t scan_code, uint32_t message_time)Queue normalized physical key press.Work code 0x0A.
Darkages.exe:0x00432D90event_queue_key_upvoid __thiscall(void *event_manager_object, uint8_t scan_code, uint32_t message_time)Queue normalized physical key release.Work code 0x0B; updates state without a pane Event.
Darkages.exe:0x00432E50event_post_socket_bytesvoid __thiscall(void *event_manager_object, const uint8_t *packet, int length)Copy and queue a decoded server packet.Work code 0x0E; preferred native-ingress block boundary and synchronized fallback injection adapter.
Darkages.exe:0x00432F10event_manager_queue_event_copyvoid __thiscall(void *event_manager_object, const void *event)Copy and queue a raw Event through EventMan.Work code 0x0F; no native call sites in this build.
Darkages.exe:0x00433110event_process_work_itemvoid __thiscall(void *event_manager_object, int code, void *data, int value)Convert EventMan records into state updates or Events.Codes 4 through 0x0B, 0x0E, and 0x0F are relevant to injection.
Darkages.exe:0x00433DC4event_queue_socket_packetvoid __stdcall(uint8_t *packet, uint32_t size)Build Event type 9 and submit it to the dispatcher.Transfers packet ownership to dispatcher cleanup.
Darkages.exe:0x00434080event_manager_periodic_noopvoid __thiscall(void *event_manager_object)Native EventMan periodic callback.Vtable slot +0x10; practical worker-affine command-pump slot.
Darkages.exe:0x0045BDC0win_main_window_procLRESULT __stdcall(HWND, UINT, WPARAM, LPARAM)Convert handled Win32 input and socket notifications.Window-faithful input injection uses PostMessageA to this procedure’s window.
Darkages.exe:0x00470F80util_memory_manager_get_instancevoid *__cdecl(void)Return the client MemoryMan singleton.Required before allocating an owned server-injection buffer.
Darkages.exe:0x00470FC0util_memory_manager_allocvoid *__thiscall(void *memory_manager, int size)Allocate through the executable’s CRT wrapper.Use for buffers that dispatcher cleanup will own.
Darkages.exe:0x004A3570net_c_queue_sendvoid __thiscall(void *socket_object, const uint8_t *packet, int16_t length)Copy and queue a logical client packet.Work code 5; transfer gate permits only 0x10 during transfer.
Darkages.exe:0x004A39C0net_poll_receivevoid __thiscall(void *socket_object)Poll Socket receive state from periodic worker execution.Vtable slot +0x10; recommended Socket command-pump hook.
Darkages.exe:0x004A54D0net_process_work_itemvoid __thiscall(void *socket_object, int code, void *data, int value)Execute Socket queue records.Code 5 calls net_c_send_packet_body and frees the queued copy.
Darkages.exe:0x004A72B4net_c_send_packet_bodyvoid __thiscall(void *socket_object, const uint8_t *packet, int16_t length)Transform, frame, and call the configured transport.Socket-worker adapter; reads but does not retain or free the input.
Darkages.exe:0x004BF440util_thread_queue_post_asyncvoid __thiscall(void *worker_object, int code, void *data, int value)Post one raw asynchronous work record.Does not copy data or wait for completion, but can block while the native ring is full.

Event Proxy IPC, Rules, and State

The Event Proxy should keep client hooks independent from the external controller. A duplex IPC service carries commands and telemetry, an in-process ruleset makes immediate decisions, and a typed snapshot service supplies current state after late attach or reconnect.

Everything on this page is a proposed proxy protocol built around confirmed 4.21 client roots and object layouts. It does not describe an IPC facility already present in the game.

High-level operation

Pipe ownership and reconnect

The injected DLL should be the named-pipe server. A practical per-process name is:

\\.\pipe\darkages-event-proxy-{process_id}

The pipe should reject remote-machine clients and grant access only to the current interactive user unless the operator explicitly configures a broader security descriptor. One connection owns mutation privileges. Additional read-only monitor connections can be added later, but they complicate rule ownership and backpressure and are not required for the first implementation.

The accept loop belongs to the DLL and survives client-controller failure:

  1. Create a pipe instance and wait for a controller.
  2. Complete a versioned handshake and allocate a new connection generation.
  3. Apply subscriptions and session rules only after validation.
  4. On clean close, broken pipe, or controller process death, remove session rules and cancel that generation’s pending commands.
  5. Return immediately to the accept state. Do not unload the DLL or remove the pass-through hooks.

Persistent rules are separate from session rules. They survive pipe disconnect and must be installed with an explicit persistence flag. With no rules, no controller, or an IPC failure, the proxy passes client traffic unchanged.

Command execution and worker wakes

The pipe service and client command execution are separate concerns. The IPC thread parses and validates a command, copies it into a bounded proxy-owned queue, records the current connection generation, and signals the owning client worker. It never invokes a live client object directly.

Proxy queueConsumerWake mechanismTypical commands
SocketSocket worker periodic pumpReleaseSemaphore(Socket->wait_handles[0], 1, NULL)SEND_CLIENT_PACKET and Socket-owned queries.
EventManEventMan worker periodic pumpReleaseSemaphore(EventMan->wait_handles[0], 1, NULL)INJECT_SERVER_PACKET and normalized INJECT_INPUT.
DispatcherDispatcher worker periodic pumpReleaseSemaphore(Dispatcher->wait_handles[0], 1, NULL)Snapshot barriers, pane actions, timers, and typed UI writes.

The generic worker explicitly tolerates a work-semaphore wake while its native ring is empty and still calls virtual slot +0x10. This lets the DLL wake the correct worker without adding an unknown native work code. The Socket slot already calls net_poll_receive, the EventMan slot is a no-op, and the dispatcher slot calls event_dispatcher_tick.

Admission holds a proxy lifecycle read guard across the final state check, root and vtable validation, queue copy, and ReleaseSemaphore. A failed wake marks the command canceled and reports wake_failed; it must not be reported as admitted. At app_shutdown entry, the DLL changes to draining, rejects new guards, and waits for existing admission guards before the client closes worker handles.

All three proxy queues must be bounded. Admission fails immediately with proxy_queue_full; it must not wait for capacity while holding an IPC session lock. A successful command has two distinct milestones:

  • admitted means the command was copied into the proxy queue and the worker was signaled.
  • executed means the target pump validated the generation and live root and invoked the client-side action.

Neither status means that TCP delivered a client packet or that a pane accepted an injected server packet. Completion records should carry the original request identifier and may arrive asynchronously after the admission response.

The 4.21 native rings are also synchronized for multiple producers, so a dedicated fallback executor may call the copied-input wrappers safely. Their full-queue wait is infinite, however. This fallback must use one executor, never the pipe thread, and must stop before EventMan and Socket teardown. The worker-affine design avoids a foreign producer being asleep inside a native queue when app_shutdown destroys it.

IPC framing

A small fixed header lets either side skip unknown message types and correlate requests with replies. All proposed IPC integers are little-endian because this is a local Win32 protocol, not the game’s network byte order.

OffsetWidthFieldMeaning
0x004magicASCII DAEP.
0x042protocol_versionIPC schema version.
0x062header_size0x28 for this header.
0x082message_typeCommand, response, telemetry, or control type.
0x0A2flagsType-specific flags. Unknown required flags reject the message.
0x0C8connection_generationChanges on every successful controller handshake.
0x148request_idNonzero on a command and copied to its response.
0x1C8sequenceMonotonic telemetry or replay sequence, zero when not applicable.
0x244payload_lengthBytes after the header.

The DLL must reject an unsupported version, a header smaller than the required fields, integer overflow in header_size + payload_length, and payloads above a configured limit before allocating memory.

Message families

MessageDirectionPurpose
HELLO / HELLO_REPLYBothNegotiate IPC version, build profile, capabilities, controller role, and connection generation.
SUBSCRIBEController to DLLSelect phases, directions, actions, Event types, origins, and payload inclusion.
EVENT_RECORDDLL to controllerReport decoded ingress, central Event dispatch, logical egress, rule decisions, or loss markers.
RULESET_REPLACEController to DLLCompile and atomically replace session or persistent rules.
SEND_CLIENT_PACKETController to DLLAdmit [client action][payload] to the Socket proxy queue for worker-affine sending.
INJECT_SERVER_PACKETController to DLLAdmit [server action][payload] to the EventMan proxy queue for worker-affine Event creation.
INJECT_INPUTController to DLLPost Win32-faithful input or use an established EventMan input queue.
SNAPSHOT_REQUEST / SNAPSHOT_RESPONSEBothReturn typed current state with a dispatch sequence boundary.
PEEK_REQUEST / PEEK_RESPONSEBothRead a bounded validated memory region or pointer path.
POKE_REQUEST / POKE_RESPONSEBothPerform an explicitly enabled, width-checked, compare-before-write mutation.
PING / PONGBothDetect a dead peer without coupling hook execution to liveness.
GAPDLL to controllerReport telemetry loss or a replay request older than the retained ring.

A packet command response reports local admission or execution only. The Socket and Winsock send paths do not provide delivery confirmation, so the IPC protocol must not describe either status as network delivery.

Nonblocking telemetry

Hooks must not write to a pipe, wait on a controller, perform name resolution, or take a lock that the IPC thread can hold. Each hook should create a compact record in a bounded multi-producer queue. The IPC thread drains records, applies subscription payload policy, and writes them to the pipe.

Every record should include:

  • global sequence number;
  • phase such as decoded_ingress, event_pre_dispatch, or logical_egress;
  • origin such as client, controller, or local_rule;
  • process and thread identifier;
  • high-resolution proxy timestamp;
  • client message timestamp when the Event contains one;
  • Event type or directional packet action;
  • rule generation and matched rule identifier;
  • original and modified lengths where applicable;
  • copied logical payload when subscribed.

When the queue is full, increment a loss counter and continue the original client call. The next successfully delivered record includes the accumulated loss count, and the IPC thread also emits GAP. Blocking the game to preserve telemetry defeats the pass-through requirement.

Local rules

The controller compiles policy in the DLL once and the hooks evaluate it without an IPC round trip. A useful rule model contains:

Rule fieldExamples
ScopeDecoded ingress, central Event, logical egress.
Origin maskNative client, controller injection, local-rule emission.
Primary discriminatorEvent type, server action, or client action.
Payload predicateMinimum length, byte equality, masked byte equality, bounded integer field.
State predicateActive client phase or validated pane-class presence. Keep state reads small and profile-backed.
ActionPass, block, mirror, bounded byte patch, or enqueue a validated packet template.
LifetimeSession or persistent.
Priority and terminal flagDeterministic ordering and whether later rules may run.

The hot representation should be immutable. Compile a replacement table on the IPC thread, validate every offset and action, then swap one pointer atomically. The old table is retired only after all hooks that acquired it have exited. This can use a small active-reader counter or an epoch scheme.

Rules must not allocate, parse regular expressions, or perform unbounded string work inside a hook. Payload predicates need a proven minimum length before every read. A patch must preserve the configured maximum length and should create a scratch copy rather than edit the client’s source buffer.

Rule-generated packets go to a proxy command queue rather than calling back into a client function while a hook is midway through its own decision. The generated command carries origin local_rule, a parent sequence, and a depth. A fixed maximum depth prevents recursive rules from emitting forever.

Late-connect state synchronization

The controller needs a baseline before it can interpret live deltas. A useful reconnect protocol is:

  1. Establish the connection and begin retaining telemetry for the new generation.
  2. Request a dispatcher-bound snapshot barrier.
  3. At a bounded point between dispatcher work items, record last_applied_event_sequence and copy the small typed state roots.
  4. Send the snapshot with its sequence and state epoch.
  5. Send retained records newer than that sequence.
  6. If the retention ring overflowed, send GAP and require a new snapshot.

The central Event hook can update last_applied_event_sequence after the original dispatch returns. Most pane state changes caused by input and server packets occur during that call. A compact snapshot taken from the dispatcher worker can therefore describe state after a known Event. The capture must still validate pointers and vtables because rendering, window processing, and teardown involve other threads.

Large or not-yet-mapped world state should not be copied without a time budget on the time-critical dispatcher worker. A later implementation can maintain a proxy-owned shadow model from decoded packets or divide large snapshots into stable chunks. The first version should favor small typed UI and character-pane state.

Runtime state and memory access

Static roots

All addresses below are for the analyzed image base 0x00400000. A profile and external controller must use loaded_module_base + rva.

VARVACurrent IDA nameState reached
Darkages.exe:0x004E2D700x000E2D70ui_bulletin_sessionActive bulletin or mail session, child history, active child, and request-wait state.
Darkages.exe:0x004E32A40x000E32A4ui_equip_panePersistent equipment identifiers, names, self-look fields, and legend child.
Darkages.exe:0x004E32C00x000E32C0event_manager_instanceEventMan input state and owned Socket pointer at object +0x68.
Darkages.exe:0x004E35640x000E3564ui_users_dialog_paneReusable users dialog and its owned row/list objects.
Darkages.exe:0x004F51AC0x000F51ACui_main_menu_paneCurrent main-menu pane, or null outside that phase.
Darkages.exe:0x004F51B00x000F51B0ui_map_paneCurrent in-game MapPane root. World collections inside it are not yet mapped.
Darkages.exe:0x004F51B40x000F51B4ui_background_paneCurrent in-game background pane.
Darkages.exe:0x004F51BC0x000F51BCnet_socket_instanceActive Socket object, transport state, framing buffers, connection flag, and transfer gate.
Darkages.exe:0x004F51C80x000F51C8ui_screen_paneActive screen pane and software cursor state.
Darkages.exe:0x004F51CC0x000F51CCui_screen_registryPacked Screen composition tree.
Darkages.exe:0x004F51D00x000F51D0event_dispatcherPacked Event pane tree, capture pane, current timer state, and dispatcher worker.
Darkages.exe:0x004F51DC0x000F51DCapp_main_windowMain HWND used for window-faithful input.
Darkages.exe:0x004FA1600x000FA160util_memory_manager_instanceClient allocator root required when a worker-injected server packet transfers to dispatcher-owned cleanup.
Darkages.exe:0x004FD6400x000FD640ui_terminal_paneActive terminal pane.

The complete Screen and Event tree algorithms and current class fields are in Runtime UI Memory Map.

Current typed snapshot coverage

State familyCurrent confidence and coverage
Active pane tree and UI stateHigh. Screen and Event trees, common bounds, relative origin, visibility, and known pane vtables are mapped.
Equipment and self-lookHigh for the documented fields, 13 equipment slots, item names, appearance bytes, and bounded strings.
Skills and spellsHigh for both 36-pointer parent arrays, populated slot objects, names, identifiers, type, slot, and cooldown state.
GameButtons contentHigh. Current content and persistent chat, status, equipment, skill, and spell pane pointers are mapped.
Bulletin and mailHigh for session state and active child ownership. Child-class field schemas vary and are only partly mapped.
Users dialogPartial. Pane visibility and owned list objects are mapped; row record internals are not.
Character statistics and ordinary item inventoryNot yet mapped to the publication threshold. The relevant panes can be found, but typed fields require another reversing pass.
Map tiles, characters, monsters, and world objectsNot yet mapped to the publication threshold beyond the ui_map_pane root and packet handlers.
Network transportHigh for Socket root, connection state, frame state, buffers, transfer gate, sequence, key, and XOR table.

The proxy protocol should expose capability bits per snapshot family. It must not return guessed fields just because an object can be reached. A raw peek can still return bytes from a validated address, but the response should label them untyped.

Pointer-path reads

IPC requests should avoid controller-supplied absolute addresses where a stable descriptor is possible. A pointer path can contain:

  1. a named profile root or module RVA;
  2. whether to dereference it;
  3. a bounded signed offset;
  4. an expected vtable or readable region class;
  5. another dereference step if required;
  6. final byte count and scalar or string interpretation.

For example, a typed path can start at ui_equip_pane, dereference the root, validate ui_equip_pane_vtable, then read the item identifier expression for a checked slot. GameButtons is reached by scanning a stable Screen or Event tree snapshot for its vtable, then following the mapped content fields.

Before every dereference, validate the memory range and overflow, and confirm class vtables where available. After copying class-specific state, re-read both the root that led to it and its vtable. Discard and retry if either changed. Packed Screen nodes use stride 0x3F and Event nodes use 0x0F; they must be parsed with unaligned-safe reads rather than native structure casts.

Peek and poke policy

PEEK_REQUEST can support a named pointer path and an explicitly enabled raw-address mode. Bound each request, validate every page as readable, and return the resolved path, bytes, and profile identity. A raw address is meaningful only for the current process and build.

POKE_REQUEST should be disabled by default. A safe typed write includes:

  • named field and exact width;
  • expected root and vtable;
  • expected old bytes for compare-before-write;
  • allowed value range or bit mask;
  • execution context, normally the dispatcher command pump for pane state;
  • a response containing whether the compare and write succeeded.

Do not permit ordinary writes to vtables, function pointers, packed-list metadata, owned pointers, executable pages, or object lifetime fields. Writing a Pane visibility byte alone is not a valid show or hide operation because Screen composition, Event eligibility, capture, timers, and owner pointers must remain consistent. Prefer an established UI method, input event, or server packet for stateful changes. An explicit unsafe developer mode may offer raw writes, but it should be clearly separate from the typed API and never enabled by a controller handshake alone.

Code-level flow

Hot-hook rule evaluation

hook entry
  -> increment active hook count
  -> acquire immutable rule pointer
  -> classify fixed fields and validate payload bounds
  -> evaluate rules in priority order
  -> try to append telemetry record
  -> release rule pointer
  -> pass, block, or call original with a scratch copy
  -> decrement active hook count

The IPC thread performs ruleset parsing and compilation. It does not hold the active rules pointer while waiting on pipe I/O. A ruleset replacement response is sent only after the new table has been validated and published.

Stable typed snapshot

controller snapshot request
  -> proxy queues dispatcher barrier command
  -> signal dispatcher wait handle 0
  -> dispatcher tick pump drains bounded command
      -> record last applied Event sequence
      -> read roots and expected vtables
      -> copy packed registry metadata and nodes
      -> copy mapped class-specific fields
      -> re-read roots, metadata, and vtables
      -> retry or mark one family unstable
  -> IPC thread serializes snapshot
  -> replay retained telemetry newer than snapshot sequence

One unstable family should not invalidate unrelated stable families. The response can mark equipment stable while reporting that a bulletin child changed during capture.

Controller disconnect

pipe read or write reports disconnect
  -> invalidate connection generation
  -> cancel pending commands from that generation
  -> atomically remove session rules
  -> retain explicitly persistent rules
  -> release queued controller responses
  -> create the next pipe instance
  -> continue client pass-through

No hook waits for this cleanup. A command executor checks the generation again immediately before any mutation so a queued command cannot run after its owner disconnected.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
Darkages.exe:0x004315B0event_dispatcher_tickvoid __thiscall(void *event_dispatcher_object)Periodic dispatcher-worker entry.Proposed bounded snapshot barrier and pane-command pump location.
Darkages.exe:0x00431B84event_dispatchint __thiscall(void *event_dispatcher_object, void *event)Deliver one logical Event.Proxy can establish an applied Event sequence around the original call.
Darkages.exe:0x00432E50event_post_socket_bytesvoid __thiscall(void *event_manager_object, const uint8_t *packet, int length)Copy decoded ingress into EventMan.Native ingress hook and synchronized, blocking fallback for INJECT_SERVER_PACKET.
Darkages.exe:0x00434080event_manager_periodic_noopvoid __thiscall(void *event_manager_object)EventMan periodic worker callback.Native no-op vtable slot used by the proposed EventMan command pump.
Darkages.exe:0x0043CF20ui_game_buttons_handle_key_eventint __thiscall(void *game_buttons_pane, void *event)Select one persistent game content pane from keyboard input.Evidence that input injection should prefer normal Event routing over pointer writes.
Darkages.exe:0x0043D164ui_game_buttons_select_contentvoid __thiscall(void *game_buttons_pane, int8_t index)Hide the old persistent content pane and show the selected one.Candidate typed action only when the pane and thread context are validated.
Darkages.exe:0x004594A0ui_hierarchy_get_nodevoid *__thiscall(void *hierarchy, int index)Compute a packed node address from list stride and index.Confirms unaligned Screen and Event node traversal.
Darkages.exe:0x00492C40ui_pane_showvoid __thiscall(void *pane)Show and invalidate a Pane.Prefer this mapped method over writing only the common visibility byte.
Darkages.exe:0x00492D50ui_pane_hidevoid __thiscall(void *pane)Hide and invalidate a Pane.Must still be coordinated with owning-pane selection state.
Darkages.exe:0x004A3570net_c_queue_sendvoid __thiscall(void *socket_object, const uint8_t *packet, int16_t length)Copy and queue logical client egress.Native egress hook and synchronized, blocking fallback for SEND_CLIENT_PACKET; returns no send result.
Darkages.exe:0x004A39C0net_poll_receivevoid __thiscall(void *socket_object)Socket periodic worker callback.Recommended Socket command-pump detour; call the original before a bounded proxy drain.
Darkages.exe:0x004BF440util_thread_queue_post_asyncvoid __thiscall(void *worker_object, int code, void *data, int value)Append one asynchronous worker record.Native ring access is synchronized but can block for capacity; raw use does not establish data ownership.

Event Proxy Version Profiles

An Event Proxy should treat every executable build as a profile, even when later clients descend from the same source tree. Function signatures can relocate well-understood symbols across nearby builds, but they cannot by themselves prove object field offsets, allocation sizes, ownership, or calling conventions.

The recommended strategy for 5.x and 7.x is to reverse one representative executable from each build family, establish the same lifecycle, Event, Socket, pane, and state roots, and generate signatures from that ground truth. Signature remapping can then handle close patch revisions. It should not replace the first semantic analysis of a major client family.

High-level operation

Exact profiles first

The resolver should fingerprint the loaded image before it installs any hook. The strongest identity is a hash of the file or its mapped immutable sections. PE timestamp, image size, section sizes, and version resources are useful secondary properties but are not unique enough alone.

The analyzed sample has SHA-256 36093dc1572521ea8c4c5f25548068c971a3d41c7722ef9fe0c05c94e24d6979. Its project identification is 4.21 Stone, while its Windows version resource reports 360, 0, 0, 0; the mismatch is documented in Analyzed Binary.

For an exact match, the profile supplies RVAs, expected instructions, vtables, globals, calling conventions, object fields, and capability-specific validators. The runtime address is always:

loaded_module_base + profile_rva

Do not store a process-specific absolute address in controller configuration.

Dark Ages 4.21 core profile

SymbolVARVAProfile role
app_shutdownDarkages.exe:0x0045B8F00x0005B8F0Draining and detach boundary.
app_initializeDarkages.exe:0x0045CCA00x0005CCA0Early-injection readiness boundary.
event_dispatcher_queue_event_copyDarkages.exe:0x004312000x00031200Copy a 36-byte Event and post dispatcher work code 3.
event_dispatcher_tickDarkages.exe:0x004315B00x000315B0Dispatcher-thread command pump.
event_dispatchDarkages.exe:0x00431B840x00031B84Central Event hook.
event_dispatch_hierarchyDarkages.exe:0x00431D540x00031D54Recursive pane delivery validator.
event_post_socket_bytesDarkages.exe:0x00432E500x00032E50Decoded server packet hook and injection.
event_manager_queue_event_copyDarkages.exe:0x00432F100x00032F10Optional raw Event injection through EventMan.
event_process_work_itemDarkages.exe:0x004331100x00033110EventMan worker-side packet and input adapter.
event_manager_periodic_noopDarkages.exe:0x004340800x00034080EventMan worker command-pump slot.
util_memory_manager_allocDarkages.exe:0x00470FC00x00070FC0Client-owned inbound packet allocation.
net_c_queue_sendDarkages.exe:0x004A35700x000A3570Logical client packet hook and native-queue fallback.
net_poll_receiveDarkages.exe:0x004A39C00x000A39C0Socket periodic callback and command-pump hook.
net_process_work_itemDarkages.exe:0x004A54D00x000A54D0Socket work-code validator.
net_c_send_packet_bodyDarkages.exe:0x004A72B40x000A72B4Transform, frame, and send validator.
event_manager_instanceDarkages.exe:0x004E32C00x000E32C0EventMan static root.
net_socket_instanceDarkages.exe:0x004F51BC0x000F51BCSocket static root.
ui_screen_registryDarkages.exe:0x004F51CC0x000F51CCScreen tree static root.
event_dispatcherDarkages.exe:0x004F51D00x000F51D0Dispatcher static root.
app_main_windowDarkages.exe:0x004F51DC0x000F51DCMain HWND root.
util_memory_manager_instanceDarkages.exe:0x004FA1600x000FA160Client allocator static root.
event_dispatcher_vtableDarkages.exe:0x0050D1800x0010D180Dispatcher class validator.
event_manager_vtableDarkages.exe:0x0050D7200x0010D720EventMan class validator.
event_vtableDarkages.exe:0x0050D7400x0010D740Raw Event object validator.
net_socket_vtableDarkages.exe:0x005269400x00126940Socket class validator.

This table is the core hook profile, not the complete memory profile. UI vtables, roots, and fields remain in Runtime UI Memory Map and Pane Virtual Table Inventory.

What a profile must contain

A useful profile contains more than symbol addresses:

Profile sectionRequired information
Image identityHash, section layout, preferred base, version metadata, and immutable-section hashes.
FunctionsRVA, prototype, calling convention, minimum detour length, expected normalized instructions, and semantic validator identifier.
GlobalsRVA, type, lifecycle, nullable phases, expected publisher and clearer functions.
VtablesRVA, slot count used by the proxy, required slot targets, and class name confidence.
Object layoutsSize where established, field offsets and widths, ownership, discriminator, valid ranges, worker wait handle 0, and native ring pointer.
Packet and Event layoutsDirection, type or action field, payload representation, owned pointers, and cleanup function.
CapabilitiesRequired symbols and validators for each hook, command, snapshot family, peek path, or typed poke.

A controller should receive the active profile identifier during HELLO_REPLY and repeat it in diagnostic logs and captured metadata. This prevents a memory path from one build being silently applied to another.

Signature scanning

A function signature is useful only when it survives relocations and still has enough structure to be unique. Generate signatures from decoded instructions, not by copying a long unexamined byte range.

The scanner should:

  1. Restrict the search to the expected PE section and executable or read-only data class.
  2. Wildcard absolute image addresses, import pointers, relative-call displacements, jump-table addresses, and other relocation-sensitive operands.
  3. Preserve stable opcodes, constants, stack cleanup, field displacements, queue codes, and branch relationships when they are part of the established contract.
  4. Require a unique candidate before semantic validation.
  5. Decode the candidate and verify its control flow, callees, globals, object fields, and ownership behavior.
  6. Resolve related symbols as a group and verify their cross-references.

A generic function prologue such as register saves and stack allocation is not a signature. A source filename or nearby assertion string is a useful anchor but is not proof. The final candidate must perform the mapped data flow.

Semantic validators for the core hooks

SymbolRequired behavior to validate in an unknown build
event_post_socket_bytesReject nonpositive length, allocate length + 1, copy bytes, append zero, and post EventMan work code 0x0E.
event_dispatchRead the dispatcher’s pane hierarchy, honor capture state, classify mouse, keyboard, and socket Events, and call the recursive hierarchy function.
event_dispatcher_queue_event_copyAllocate the established Event size, shallow-copy the complete Event, and post dispatcher work code 3.
net_c_queue_sendAccept a pointer and signed length, enforce the transfer exception for action 0x10, allocate length + 1, append zero, and post Socket work code 5.
net_process_work_itemRoute code 5 to the logical body sender and free the queued packet copy afterward.
net_poll_receive and Socket vtableOccupy periodic slot +0x10, run on the Socket worker after every wake, and poll the configured receive path.
event_manager_periodic_noop and EventMan vtableOccupy periodic slot +0x10 and return without native work.
util_thread_queue_worker_loopTreat an empty wait-handle-0 wake as valid and still call periodic slot +0x10.
util_ring_buffer_push_waitLock the queue monitor, wait on not-full at capacity, copy one fixed-size element, signal not-empty, and unlock.
event_manager_ctor and rootsAllocate or construct the Socket at EventMan +0x68, publish EventMan and Socket globals, and clear them during destruction.
app_shutdownDelete EventMan before final dispatcher destruction; EventMan’s destructor deletes its Socket and clears both static roots.

Validation should also confirm the expected vtable at each live root. A matching function body with a mismatched owner layout is not enough to enable mutation.

Capability gating

Resolve capabilities independently, then expose only the ones whose complete dependency set passed:

CapabilityMinimum validated dependencies
Decoded ingress observation and blockevent_post_socket_bytes, EventMan root and vtable, safe detour bytes.
Central Event observation and blockevent_dispatch, Event layout, dispatcher root and vtable, caller cleanup.
Client packet observationnet_c_queue_send, Socket root and vtable, signed length and transfer-gate fields.
Worker-affine client packet sendSocket root and vtable, net_poll_receive, net_c_send_packet_body, wait handle 0, signed length, and transfer-gate fields.
Worker-affine server packet injectionEventMan root and vtable, periodic slot, event_process_work_item, memory-manager root and allocator, packet ownership, and wait handle 0.
Window-faithful inputMain HWND, window procedure behavior, message schema.
Internal input injectionEventMan root plus each requested input queue function and normalized scan-code contract.
Typed UI snapshotRoot or tree walker, vtable, every requested field offset, stable-copy rules.
Typed pokeAll snapshot dependencies plus an established mutation method or safe scalar field contract.

If a required mutation dependency fails, disable that capability. Do not fall back to an unvalidated nearest address. Passive build diagnostics can remain available without client hooks.

Porting to 5.x and 7.x

Why one-time reversing is still required

Shared source lineage is valuable because queue codes, class roles, packet actions, assertion text, vtable shapes, and ownership patterns may remain recognizable. It does not guarantee stable machine code or memory layouts. These changes can invalidate a 4.21 profile while leaving the game visibly similar:

  • compiler or optimization changes;
  • inserted class fields that shift every later offset;
  • new virtual methods that shift vtable slots;
  • inlining or outlining of queue wrappers;
  • different static initialization and linker ordering;
  • larger packet, map, UI, or transport buffers;
  • changed ownership or thread-affinity rules;
  • security additions around packet processing.

Function scanning also does not find heap fields automatically. Character statistics, inventory, and world-object walking require profile-specific owner roots, object discriminators, container layouts, and deletion rules. These should be established once for each family and then compared structurally with 4.21.

  1. Identify the exact executable and record its hash without adding it to the repository.
  2. Map WinMain, app_initialize, the message procedure, and app_shutdown.
  3. Locate the generic worker queue and distinguish the dispatcher, EventMan, and Socket objects.
  4. Trace decoded receive bytes into pane delivery and packet builders into the logical send queue.
  5. Establish Event type layout, Event cleanup, pane handler vslots, and timer delivery.
  6. Find EventMan, Socket, dispatcher, Screen, MapPane, and persistent character-pane roots.
  7. Map state fields from packet handlers and multiple callers, not from string proximity.
  8. Update IDA names, types, comments, and vtable slots.
  9. Produce an exact profile and instruction-aware signatures from the established functions.
  10. Run passive telemetry first. Enable blocking, injection, and writes only after runtime observations agree with the static model.

Start with one representative 5.x build and one representative 7.x build. If several patch revisions differ only slightly, signatures and semantic validators can likely cover them. If allocation sizes, vtable layouts, or packet ownership change, split them into separate profiles even when the major version is the same.

Comparing profiles

Use semantic identifiers across profiles while storing version-specific details beneath them. For example, all profiles can export logical_client_send, but each entry supplies its own RVA, ABI, transfer-gate offset, sentinel rule, and validators. The IPC command stays stable while the injected adapter changes by profile.

For memory, compare paths rather than absolute offsets alone:

semantic root -> owner relationship -> container kind -> element discriminator -> field

If EventMan -> owned Socket remains true but the owner offset changes, the profile records the new offset. If a later client moves the Socket to another singleton, the semantic path changes and must be re-established rather than forced into the old shape.

Code-level flow

Resolver flow

load image metadata
  -> exact immutable-section hash match
      -> load exact profile
  -> otherwise scan instruction-aware signatures
      -> collect unique candidates
      -> run per-symbol semantic validators
      -> validate roots, vtables, and cross-references
      -> build a temporary resolved profile
  -> evaluate capability dependency sets
  -> install only fully validated capabilities

The temporary resolved profile should be exportable as diagnostic data, but it should not be promoted to a maintained profile until the same addresses and layouts are confirmed in IDA and, where needed, by controlled runtime observation.

Normalized instruction matching

For matching purposes, represent each instruction by opcode class and stable operand properties. Relative targets can be expressed as “call to candidate with validator X” rather than a fixed displacement. Absolute globals can be expressed as “writable image address published by constructor Y.” This is more durable than a byte mask alone and permits cross-symbol validation.

A byte mask can remain the fast first stage. The instruction model is the proof stage before hook installation.

Field-offset recovery

Do not copy 4.21 offsets into an unknown profile. Recover offsets from established functions and require agreement across independent uses. Examples include:

  • Event type reads in all three classifier functions;
  • packet pointer and size writes in socket Event construction and reads in dispatcher cleanup;
  • Socket transfer-gate reads in logical send queueing;
  • Socket connection flag writes in connect, receive-zero, and disconnect paths;
  • Pane visibility reads, show writes, and hide writes;
  • parent slot-array reads in add, remove, destructor, and activation paths.

Only publish a field after its owner, width, signedness, and lifecycle agree across the relevant sites.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
Darkages.exe:0x00431200event_dispatcher_queue_event_copyvoid __thiscall(void *event_dispatcher_object, const void *event)Copy one established Event into dispatcher work.Signature validator includes 36-byte copy and work code 3.
Darkages.exe:0x00431B84event_dispatchint __thiscall(void *event_dispatcher_object, void *event)Central logical Event routing.Signature validator includes capture, hierarchy, and three Event category tests.
Darkages.exe:0x00432E50event_post_socket_bytesvoid __thiscall(void *event_manager_object, const uint8_t *packet, int length)Copy decoded server bytes into EventMan work.Signature validator includes length + 1, zero sentinel, and code 0x0E.
Darkages.exe:0x00432630event_manager_ctorvoid *__thiscall(void *event_manager_object)Construct EventMan and its owned Socket.Allocates the 0x780D4-byte Socket, stores it at +0x68, and publishes both roots in 4.21.
Darkages.exe:0x00433110event_process_work_itemvoid __thiscall(void *event_manager_object, int code, void *data, int value)Execute EventMan worker records.Code 0x0E transfers a client-allocated decoded packet into Event type 9.
Darkages.exe:0x00434080event_manager_periodic_noopvoid __thiscall(void *event_manager_object)Native EventMan periodic callback.Confirms a no-op +0x10 worker slot for a profile-gated command pump.
Darkages.exe:0x0045B8F0app_shutdownvoid __cdecl(void)Destroy global subsystems.Cross-profile lifecycle and detach anchor.
Darkages.exe:0x0045CCA0app_initializevoid __cdecl(void)Build the main client subsystem graph.Cross-profile allocation and publication anchor.
Darkages.exe:0x00470FC0util_memory_manager_allocvoid *__thiscall(void *memory_manager, int size)Allocate client-owned storage.Required for EventMan worker injection whose payload reaches dispatcher cleanup.
Darkages.exe:0x004A32F0net_socket_ctorvoid *__thiscall(void *socket_object, void *event_sink)Construct Socket state and worker base.Vtable, buffer-size, event-sink, and default-key anchor.
Darkages.exe:0x004A3570net_c_queue_sendvoid __thiscall(void *socket_object, const uint8_t *packet, int16_t length)Copy logical client egress.Signature validator includes transfer gate, 0x10 exception, sentinel, and work code 5.
Darkages.exe:0x004A39C0net_poll_receivevoid __thiscall(void *socket_object)Run Socket periodic receive polling.Socket vtable slot +0x10; profile-gated Socket command-pump hook.
Darkages.exe:0x004A54D0net_process_work_itemvoid __thiscall(void *socket_object, int code, void *data, int value)Dispatch Socket worker records.Validates queue code relationships and queued-buffer cleanup.
Darkages.exe:0x004A72B4net_c_send_packet_bodyvoid __thiscall(void *socket_object, const uint8_t *packet, int16_t length)Transform, frame, and send on the Socket worker.Worker-affine injection adapter; validate that it does not retain or free its input.
Darkages.exe:0x00498080util_ring_buffer_push_waitvoid __thiscall(void *ring_buffer, const void *element)Synchronize bounded-ring admission.Cross-family proof that native multi-producer admission is race-safe but can block.
Darkages.exe:0x004BF440util_thread_queue_post_asyncvoid __thiscall(void *worker_object, int code, void *data, int value)Append a raw async record and signal the worker.Cross-family worker-queue anchor; can block for capacity and caller wrappers establish ownership.

Format Catalog

This catalog will link to one page per understood file or container format.

Each format page must explain its purpose and loading flow, then record byte order, complete field layout, string rules, validation, reader and writer call paths, and relevant function addresses. Unknown fields must retain their exact offset and width.

Catalog

FormatPurposeReaderWriterDetailed page
None documented

Protocol Catalog

The Stone client uses a direction-sensitive action byte inside an 0xAA binary frame. This catalog separates client actions from server actions because equal numeric values can have unrelated behavior.

Transport and framing

The standard network transport is an IPv4 TCP byte stream. Frames have this wire layout:

AA [uint16 body_length, big-endian] [body bytes...]
OffsetWidthMeaning
0x0010xAA frame marker.
0x012Unsigned big-endian body length.
0x031Direction-specific action.
0x04variableTransformation metadata or payload, depending on action.

body_length includes the action and every remaining wire body byte. It excludes the marker and its own two bytes. Total frame length is body_length + 3.

Ordinary transformed bodies have this form:

[action] [sequence] [XOR-transformed payload and zero sentinel]

The logical packet delivered to or accepted from application code is:

[action] [payload] [zero sentinel]

The sequence byte and XOR passes are omitted for client actions 0x00, 0x10, and 0x48, and for server actions 0x00, 0x03, and 0x40. Only transformed client packets consume and increment the client send sequence. See Sequence and XOR Transformation for the exact pass order, negotiated key, and generated-table formulas.

Directional catalogs

DirectionFixed action valuesIndex
Client to server51Client actions, with one linked page per action
Server to client59Server actions, with one linked page per action

Thirty-five action values occur in both lists. Direction is part of a packet’s identity.

The server action 0x4B contains a big-endian length followed by a logical client packet. The client forwards that embedded packet through its normal send queue. This permits a server-selected action in addition to the 51 constants present in client packet builders.

Current interpretation level

The indexes distinguish established action support from payload details that still require tracing:

  • Direction-prefixed C... and S... class names identify messages in the 4.21 protocol catalog.
  • Payload fields and behavioral meanings are documented only when instructions, data flow, diagnostics, or visible state changes establish them.
  • Builder and handler addresses are included even when individual payload fields remain to be analyzed.

The indexes are exhaustive for fixed action constants accepted or emitted by the traced Stone packet path. They are not claims that every payload schema is already understood.

Direction-specific network-owned IDA names use net_c_... for client-to-server code and net_s_... for server-to-client code. Direction-neutral transport code retains net_.... Methods owned primarily by another subsystem retain that subsystem prefix, such as ui_... for pane packet dispatch.

Sequence and XOR Transformation

Most binary packets carry a sequence byte and use three XOR passes over the payload plus a trailing zero. The message indexes call this Encrypted: Yes for protocol familiarity. It is obfuscation rather than authenticated encryption: there is no integrity tag, message authentication code, or cryptographic security boundary.

Wire placement

The transformation applies inside the standard 0xAA frame:

Encrypted:     AA [uint16 body_length, big-endian] [action] [sequence] [XOR(payload || 00)]
Not encrypted: AA [uint16 body_length, big-endian] [action] [payload] [00]

body_length counts the action and every subsequent body byte. It excludes the marker and the two-byte length. The action remains visible in both forms. Only the payload and trailing zero enter the XOR passes.

These actions bypass the sequence and XOR code:

DirectionEncrypted: No
Client to server0x00, 0x10, 0x48
Server to client0x00, 0x03, 0x40

All other fixed actions in the current indexes use the encrypted branch.

Runtime state and defaults

StateDefaultBehavior
Repeating keyNine ASCII bytes NexonInc.Repeats across the transformed data.
Table function0Produces the identity table, where the generated byte equals input_value.
Client send sequence0x00Inserted after the action and incremented modulo 256 after each encrypted client packet.

Clear client packets do not consume the client send sequence. On receive, the decoder uses the sequence carried by the server packet. No independent receive counter or monotonic-sequence check appears in the decoded server path.

Server-controlled key and table

The server can replace both transformation inputs during the clear SVersionCheck action 0x00. Subtype 0x00 has this payload prefix:

Payload offsetWidthFieldBehavior
0x001subtypeConstant 0x00 for transformation negotiation.
0x011server_versionStored and compared with the active server version.
0x021table_functionQueues regeneration with function index 0 through 9.
0x031key_lengthThe main-menu handler requires exactly 9.
0x04key_lengthkeyCopied and queued as the new repeating key.

ui_main_menu_handle_server_packet queues the table update through net_queue_xor_table_function and the key update through net_queue_xor_key. Socket work codes 11 and 10 apply them in net_process_work_item. The general setter accepts a key length no greater than nine, but this server-negotiation path asserts that the supplied length equals nine.

Because action 0x00 is not encrypted, these values can establish new state without depending on the previous key or table.

Client encoding

The encoder produces:

[action] [packet_sequence] [encoded payload and trailing zero]

It copies packet_sequence to the output, increments the stored counter modulo 256, and transforms payload_and_zero in this order:

void encode_packet_data(
    const uint8_t *payload_and_zero,
    size_t data_length,
    const uint8_t *repeating_key,
    size_t key_length,
    const uint8_t *xor_table,
    uint8_t packet_sequence,
    uint8_t *encoded_data)
{
    size_t byte_index;
    size_t block_index;
    size_t key_index;
    size_t data_index;
    uint8_t block_number;

    for (byte_index = 0; byte_index < data_length; ++byte_index) {
        encoded_data[byte_index] =
            payload_and_zero[byte_index] ^
            repeating_key[byte_index % key_length];
    }

    for (block_index = 0;
         block_index * key_length < data_length;
         ++block_index) {
        block_number = (uint8_t)block_index;
        if (block_number == packet_sequence)
            continue;

        for (key_index = 0; key_index < key_length; ++key_index) {
            data_index = block_index * key_length + key_index;
            if (data_index >= data_length)
                break;

            encoded_data[data_index] ^=
                xor_table[block_number + key_index];
        }
    }

    for (byte_index = 0; byte_index < data_length; ++byte_index) {
        encoded_data[byte_index] ^=
            xor_table[packet_sequence + byte_index];
    }
}

The first pass uses the repeating nine-byte key. The second applies table bytes by key-sized block, except when the block number equals the packet sequence. The third applies table bytes indexed from the sequence.

Server decoding

net_s_decode_packet_body leaves the action in place, reads the following sequence byte, and reverses the XOR passes in the opposite order:

  1. XOR with table bytes indexed from the received sequence.
  2. XOR each key-sized block with its table bytes, skipping the block whose byte index equals the received sequence.
  3. XOR with the repeating key.
  4. Remove the sequence byte from the application-facing packet.

The decoder returns one fewer byte than the wire body because the sequence is removed. It also writes an additional zero immediately after the returned bytes.

Trailing zero behavior

net_c_queue_send appends a zero before client encoding, so the trailing zero is transformed with the payload. The server is expected to provide the corresponding byte on server packets.

The 4.21 decoder does not verify that this byte becomes zero. It neither rejects the packet nor reports a transformation failure when the decoded value is nonzero. It keeps that byte in the returned logical packet and writes another local zero guard after it. A proxy or compatible server can use the decoded value as a diagnostic, but treating it as a required integrity check would be stricter than this client.

Table generators

net_build_xor_table accepts function indexes 0 through 9. For input_value from 0 through 255, it generates the following byte values. Function 9 first calculates centered_value as ((int32_t)input_value - 128) / 8, using signed C integer division toward zero.

FunctionC expression for the generated byte
0input_value
1128 + (((input_value & 1) == 0) ? ((input_value + 1) / 2) : -((input_value + 1) / 2))
2255 - input_value
3128 + (((input_value & 1) == 0) ? ((255 - input_value) / 2) : -((255 - input_value) / 2))
4(input_value / 16) * (input_value / 16)
5(2 * input_value) & 0xFF
6255 - ((2 * input_value) & 0xFF)
7(input_value <= 127) ? (255 - 2 * input_value) : (2 * input_value - 256)
8(input_value <= 127) ? (2 * input_value) : (511 - 2 * input_value)
9255 - ((centered_value * centered_value) & 0xFF)

The image starts with function 0. Each generated byte is stored in all four bytes of one dword, allowing the optimized paths to XOR four data bytes at once.

The additive table indexes in the encoder and decoder do not have an explicit modulo operation or bounds check after addition. Compatible implementations should reproduce the observed indexing before adding stricter validation.

Function reference

AddressCurrent IDA namePrototypePurpose
Darkages.exe:0x0045F780ui_main_menu_handle_server_packetint __thiscall(void *this, void *event)Parse clear action 0x00, including the table selector and negotiated key.
Darkages.exe:0x004A36C0net_queue_xor_keyvoid __thiscall(void *this, unsigned int key_length, const uint8_t *key)Copy and queue a key update as Socket work code 10.
Darkages.exe:0x004A3700net_queue_xor_table_functionvoid __thiscall(void *this, unsigned int function_index)Queue table regeneration as Socket work code 11.
Darkages.exe:0x004A44D4net_s_decode_packet_bodyint __stdcall(const uint8_t *src, int src_size, uint8_t *dest)Decode a sequence-bearing server body.
Darkages.exe:0x004A54D0net_process_work_itemvoid __thiscall(void *this, int code, void *data, int value)Apply queued key and table updates on work codes 10 and 11.
Darkages.exe:0x004A79E4net_c_encode_packet_bodyint __stdcall(const uint8_t *src, int src_size, uint8_t *dest)Insert the client sequence and encode the payload and trailing zero.
Darkages.exe:0x004A8414net_set_xor_keyvoid __stdcall(int length, const uint8_t *key)Install and repeat the active key.
Darkages.exe:0x004A8590net_build_xor_tablevoid __stdcall(int function_index)Generate the table with one of ten functions.

Client Actions

This index records every fixed action byte placed in a logical client packet before net_c_queue_send. The send-site addresses are the calls that submit the completed byte array.

Each class name in the index links to a dedicated page containing its payload format, queue call sites, and current schema status. Message-page offsets are payload-relative and exclude framing, action, sequence, and the trailing zero.

The C... names identify 52 fixed client-to-server actions found in the traced 4.21 send path. Fifty-one appear directly at net_c_queue_send call sites. CUseSpell action 0x0F is additionally established through the spell-delay controller, which stores the already-built logical packet and submits it immediately or on a later timer callback. The spelling CNewUserApperance is the 4.21 class name.

All entries use the client-to-server direction. The send queue appends a zero sentinel. Except for actions 0x00, 0x48, and 0x10, net_c_encode_packet_body also inserts a sequence byte, applies the XOR transformation, and increments the send sequence before framing. The three cleartext bypass actions neither carry nor consume a sequence value.

Fixed action index

ActionClass nameEncrypted4.21 send sites and notes
0x00CVersionNoDarkages.exe:0x004AD6A3. The four-byte payload is a big-endian data-version uint16_t followed by ASCII LK. XOR bypass. Used during login or lobby.
0x02CNewUserYesDarkages.exe:0x00462730. Used during login or lobby; the payload semantics are not yet established.
0x03CLoginYesDarkages.exe:0x00462D2F. Used during login or lobby; the payload semantics are not yet established.
0x04CNewUserApperanceYesDarkages.exe:0x00461725. Used during login or lobby; the payload semantics are not yet established.
0x05CMapRequestYesDarkages.exe:0x00465D7A. Payload semantics are not yet established.
0x06CMoveYesDarkages.exe:0x0048853A. Payload semantics are not yet established.
0x07CGetYesDarkages.exe:0x00454213. Payload semantics are not yet established.
0x08CDropYesDarkages.exe:0x004530AE. Payload semantics are not yet established.
0x0BCQuitYesDarkages.exe:0x0046471C, Darkages.exe:0x004889DC, Darkages.exe:0x00488E2E, Darkages.exe:0x004922B1, Darkages.exe:0x0049236B, Darkages.exe:0x0049544D.
0x0CCPutGroundYesDarkages.exe:0x0046B1C2. Payload semantics are not yet established.
0x0DCBlockListenYesDarkages.exe:0x004C1691, Darkages.exe:0x004C1956, Darkages.exe:0x004C1A66.
0x0ECSayYesDarkages.exe:0x004811F1, Darkages.exe:0x00482DA9, Darkages.exe:0x0048865F, Darkages.exe:0x004C0987.
0x0FCUseSpellYesui_spell_slot_begin_cast builds the confirmed two-byte immediate form. ui_spell_delay_begin queues it directly or ui_spell_delay_handle_timer queues the held packet after delay traffic.
0x10CTransferServerNoDarkages.exe:0x00460617, Darkages.exe:0x00463119, Darkages.exe:0x0046C901. A MapPane::ProcessTransferServer diagnostic names kClientTransferServer; this is the only action allowed while the Socket transfer gate is active. XOR bypass.
0x11CChangeDirectionYesDarkages.exe:0x0046822F, Darkages.exe:0x004884D4.
0x13CAttackYesDarkages.exe:0x0048895C. Payload semantics are not yet established.
0x18CWhoYesnet_c_send_who_request submits one action byte. The E window button requests the list; SShowUsers populates and shows the pane.
0x19CWhisperYesDarkages.exe:0x004C1182. Payload semantics are not yet established.
0x1BCUserSettingYesnet_c_send_user_setting submits one setting-code byte from OptionPane-family controls.
0x1CCUseYesDarkages.exe:0x004533C6. Payload semantics are not yet established.
0x1DCEmotionYesDarkages.exe:0x00488694. Payload semantics are not yet established.
0x23CExitEditingModeYesui_paper_dialog_handle_completion and net_c_send_exit_editing_mode serialize Paper mode, text length, and normalized text.
0x24CDropGoldYesDarkages.exe:0x004534E1. Payload semantics are not yet established.
0x26CChangePasswordYesDarkages.exe:0x00463A45, Darkages.exe:0x004642F0. Used during login or lobby.
0x29CGiveYesDarkages.exe:0x0045336A. Payload semantics are not yet established.
0x2ACGiveGoldYesDarkages.exe:0x0045360B. Payload semantics are not yet established.
0x2DCSelfLookYesnet_c_send_self_look submits one action byte when a hidden current equipment pane is reopened.
0x2ECGroupYesDarkages.exe:0x004306AE. Payload semantics are not yet established.
0x2FCGroupToggleYesDarkages.exe:0x0042F1D0. Payload semantics are not yet established.
0x30CChangeSlotYesDarkages.exe:0x0042499F, Darkages.exe:0x00441216, Darkages.exe:0x004427DF, Darkages.exe:0x0044395F.
0x38CRefreshUserYesDarkages.exe:0x00488110. Payload semantics are not yet established.
0x39CMenuCodeYesDarkages.exe:0x0047B8CD, Darkages.exe:0x0047BBB7, Darkages.exe:0x0047C175, Darkages.exe:0x0047C2CF, Darkages.exe:0x0047C60F, Darkages.exe:0x0047C8C8, Darkages.exe:0x0047D3BF, Darkages.exe:0x0047D46D, Darkages.exe:0x0047D7DC.
0x3ACMessageYesDarkages.exe:0x0047E85C, Darkages.exe:0x0047F9AC, Darkages.exe:0x0047FA89, Darkages.exe:0x004805CC, Darkages.exe:0x004806A9, Darkages.exe:0x0048113C, Darkages.exe:0x004812AE, Darkages.exe:0x00481EEC, Darkages.exe:0x0048205B, Darkages.exe:0x00482B5C, Darkages.exe:0x00482F1C.
0x3BCBulletinYesDarkages.exe:0x0040ECE1, Darkages.exe:0x004109EC, Darkages.exe:0x0041294C, Darkages.exe:0x004145B4, Darkages.exe:0x00415738, Darkages.exe:0x00415934, Darkages.exe:0x0041715C, Darkages.exe:0x0041817E, Darkages.exe:0x004187E8, Darkages.exe:0x0041A73C, Darkages.exe:0x0041AE45, Darkages.exe:0x0041B4E4, Darkages.exe:0x0041B60B, Darkages.exe:0x0041B8A8, Darkages.exe:0x0041BA1B.
0x3CCPutToContainerYesDarkages.exe:0x00424B24. Payload semantics are not yet established.
0x3DCGetFromContainerYesDarkages.exe:0x004412D8, Darkages.exe:0x00453A15.
0x3ECUseSkillYesnet_c_send_use_skill submits [0x3E, skill_slot].
0x3FCFieldMapYesDarkages.exe:0x0043953A. Payload semantics are not yet established.
0x41CGetParcelYesDarkages.exe:0x00494E2C. Payload semantics are not yet established.
0x42CExceptionYesDarkages.exe:0x00434B43. Payload semantics are not yet established.
0x43CRequestObjectInfoYesDarkages.exe:0x0047B755, Darkages.exe:0x00488723, Darkages.exe:0x0048C820, Darkages.exe:0x0048F17F.
0x44CRemoveEquipYesDarkages.exe:0x0042F8E2. Payload semantics are not yet established.
0x45CReplyCRCYesDarkages.exe:0x00465522. Payload semantics are not yet established.
0x46CGroupViewYesDarkages.exe:0x0042F919. Payload semantics are not yet established.
0x47CAddStatYesDarkages.exe:0x0044028E. Payload semantics are not yet established.
0x48CRequestPatchNoDarkages.exe:0x0048763C, Darkages.exe:0x004953EC, Darkages.exe:0x00495F55, Darkages.exe:0x0049609D. XOR bypass. May be used during login or lobby.
0x4ACExchangeYesDarkages.exe:0x0043071A, Darkages.exe:0x0043596E, Darkages.exe:0x00435A5A, Darkages.exe:0x00435ACA, Darkages.exe:0x00435E90, Darkages.exe:0x00437310, Darkages.exe:0x00437AE8.
0x4DCSpellDelayRequestYesnet_c_send_spell_delay_request submits the configured delay-seconds byte.
0x4ECSpellDelaySayYesSkill and spell paths submit a one-byte phrase length followed by phrase bytes; the delayed spell path can repeat it on 1000 ms timers.
0x4FCSendPortraitYesDarkages.exe:0x0040A7E3. net_c_build_portrait_response reads and validates the local portrait and includes its bytes in the response to server action 0x49.
0x57CMultiYesDarkages.exe:0x004A29D7, Darkages.exe:0x004A2EB0.
0x62literal baramYesDarkages.exe:0x004AD50B. The five submitted logical bytes are the literal ASCII string baram, so byte zero is action 0x62. This special packet is named by its literal bytes rather than the C... convention.

baram bootstrap packet

Darkages.exe:0x004AD4E4 formats the literal ASCII string baram and submits exactly five bytes at Darkages.exe:0x004AD50B. The same control-flow branch immediately builds and queues CVersion at Darkages.exe:0x004AD6A3.

Action 0x62 is not one of the client XOR bypass values, so net_c_encode_packet_body inserts the current sequence byte and increments net_c_send_sequence when transmitting it. The global occupies uninitialized image storage and is therefore zero-filled when the process loads. On the initial startup path, baram carries sequence 0x00 and advances the client counter to 0x01; CVersion then bypasses the sequence machinery. The only code references to the counter are the read and increment inside net_c_encode_packet_body, so repeating this path does not explicitly reset the client counter. The packet may reset or synchronize server-side tracking, but that effect is not visible in this client code.

Server-directed dynamic send

Server action 0x4B is handled by Darkages.exe:0x0046CA54 net_forward_embedded_client_packet. It reads a big-endian uint16_t from server-packet offset 0x01, treats bytes beginning at offset 0x03 as a logical client packet of that length, and submits them to net_c_queue_send. The embedded first byte therefore selects the client action dynamically.

This path still receives the normal zero sentinel, direction-specific transformation, frame header, and transfer-state gate. It does not bypass the Socket send machinery.

CVersion (0x00)

Client action index | Next: CNewUser

CVersion is client-to-server action 0x00 in the 4.21 protocol.

Direction: client to server

Encrypted: No. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x002data_versionUnsigned big-endian value selected by the lobby startup code.
0x022client_tagLiteral ASCII LK.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004AD6A3sub_4AD140Darkages.exe:0x004AD140

Schema status

The builder establishes the complete four-byte payload. It submits the action followed by these four bytes before the send queue adds the trailing zero.

CNewUser (0x02)

Previous: CVersion | Client action index | Next: CLogin

CNewUser is client-to-server action 0x02 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00462730sub_4625C4Darkages.exe:0x004625C4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CLogin (0x03)

Previous: CNewUser | Client action index | Next: CNewUserApperance

CLogin is client-to-server action 0x03 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00462D2Fsub_462BF4Darkages.exe:0x00462BF4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CNewUserApperance (0x04)

Previous: CLogin | Client action index | Next: CMapRequest

CNewUserApperance is client-to-server action 0x04 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00461725sub_4616A4Darkages.exe:0x004616A4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CMapRequest (0x05)

Previous: CNewUserApperance | Client action index | Next: CMove

CMapRequest is client-to-server action 0x05 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00465D7Asub_465CC4Darkages.exe:0x00465CC4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CMove (0x06)

Previous: CMapRequest | Client action index | Next: CGet

CMove is client-to-server action 0x06 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001directionDirection byte supplied by the local-user movement path.
0x011move_sequenceClient movement sequence. The sender increments local user +0x6F28 before writing it.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0048853Anet_c_send_moveDarkages.exe:0x004884E4

Sender notes

net_c_send_move creates the complete three-byte logical packet: action 0x06, direction, and move_sequence. It submits length 3 to net_c_queue_send; the queue adds the trailing zero separately.

The sender also records timeGetTime at local user +0x6F2C. A later SMove uses this value for the lag indicator. Only one timestamp is retained, so a subsequent move replaces the timestamp even if an earlier server response remains outstanding.

Schema status

The complete payload and queue length are established directly from the 4.21 builder. Direction validation occurs in its callers and movement state code, not in this two-field serializer.

CGet (0x07)

Previous: CMove | Client action index | Next: CDrop

CGet is client-to-server action 0x07 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00454213sub_4541B4Darkages.exe:0x004541B4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CDrop (0x08)

Previous: CGet | Client action index | Next: CQuit

CDrop is client-to-server action 0x08 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004530AEsub_453054Darkages.exe:0x00453054

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CQuit (0x0B)

Previous: CDrop | Client action index | Next: CPutGround

CQuit is client-to-server action 0x0B in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001subtypeThe exit-wait constructor sends 0x01; the SReconnect subtype-one response sends 0x00. Values used by the other builders are not yet compared.
0x01payload_length - 1unknown_01Any additional payload bytes.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0046471Csub_4646F0Darkages.exe:0x004646F0
Darkages.exe:0x004889DCsub_488974Darkages.exe:0x00488974
Darkages.exe:0x00488E2Esub_488B44Darkages.exe:0x00488B44
Darkages.exe:0x004922B1ui_exit_wait_handle_server_packetDarkages.exe:0x004921F0
Darkages.exe:0x0049236Bui_exit_wait_pane_ctorDarkages.exe:0x00492310
Darkages.exe:0x0049544Dsub_495414Darkages.exe:0x00495414

Schema status

The two exit-wait call sites establish the subtype exchange. The remaining four builders still need to be compared before assigning a complete subtype directory.

CPutGround (0x0C)

Previous: CQuit | Client action index | Next: CBlockListen

CPutGround is client-to-server action 0x0C in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0046B1C2sub_46B194Darkages.exe:0x0046B194

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CBlockListen (0x0D)

Previous: CPutGround | Client action index | Next: CSay

CBlockListen is client-to-server action 0x0D in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004C1691sub_4C1654Darkages.exe:0x004C1654
Darkages.exe:0x004C1956sub_4C18A4Darkages.exe:0x004C18A4
Darkages.exe:0x004C1A66sub_4C19B4Darkages.exe:0x004C19B4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CSay (0x0E)

Previous: CBlockListen | Client action index | Next: CUseSpell

CSay is client-to-server action 0x0E in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004811F1sub_481150Darkages.exe:0x00481150
Darkages.exe:0x00482DA9sub_482B70Darkages.exe:0x00482B70
Darkages.exe:0x0048865Fsub_4885D4Darkages.exe:0x004885D4
Darkages.exe:0x004C0987sub_4C0920Darkages.exe:0x004C0920

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CUseSpell (0x0F)

Previous: CSay | Client action index | Next: CTransferServer

CUseSpell is the documentation name for client-to-server action 0x0F in the 4.21 client. The friendly source class name is not retained, but the action’s spell-slot construction and delayed submission are established directly.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Confirmed immediate payload form

OffsetWidthFieldEstablished meaning
0x001spell_slotSlot byte read from the activated spell pane at object +0xF4.

Darkages.exe:0x00455684 ui_spell_slot_begin_cast builds exactly two logical bytes, [0x0F, spell_slot], for the confirmed immediate path. Other targeting modes enter separate input panes; their final packet fields are not yet mapped.

Delayed submission

ui_spell_delay_begin copies the complete logical packet and its length into the delay controller before deciding when to send it. With a zero delay it calls net_c_queue_send immediately. With a nonzero delay it sends CSpellDelayRequest 0x4D, emits configured CSpellDelaySay 0x4E phrases on 1000 ms timers, and queues the held 0x0F packet on the final tick.

The delay phrases come from SpellBook.cfg and are matched to the spell name. This local delay means the final CUseSpell submission may be owned by a timer callback rather than the original mouse Event.

Functions

AddressCurrent IDA nameRole
Darkages.exe:0x00455684ui_spell_slot_begin_castBuild the confirmed [0x0F, slot] form.
Darkages.exe:0x004556F4ui_spell_delay_beginCopy and hold the logical packet, or submit it immediately.
Darkages.exe:0x00456940ui_spell_delay_handle_timerSubmit the held packet on the final delay tick.

See UI, Input, and Packet Flows for the input-to-timer-to-network path.

CTransferServer (0x10)

Previous: CUseSpell | Client action index | Next: CChangeDirection

CTransferServer is client-to-server action 0x10 in the 4.21 protocol.

Direction: client to server

Encrypted: No. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00460617sub_460584Darkages.exe:0x00460584
Darkages.exe:0x00463119sub_463010Darkages.exe:0x00463010
Darkages.exe:0x0046C901sub_46C7D4Darkages.exe:0x0046C7D4

Schema status

The payload fields remain placeholders. Stone diagnostics independently name kClientTransferServer, and the Socket transfer gate permits only this action while a server transfer is active.

CChangeDirection (0x11)

Previous: CTransferServer | Client action index | Next: CAttack

CChangeDirection is client-to-server action 0x11 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0046822Fsub_4681E4Darkages.exe:0x004681E4
Darkages.exe:0x004884D4sub_4884B4Darkages.exe:0x004884B4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CAttack (0x13)

Previous: CChangeDirection | Client action index | Next: CWho

CAttack is client-to-server action 0x13 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0048895Csub_488944Darkages.exe:0x00488944

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CWho (0x18)

Previous: CAttack | Client action index | Next: CWhisper

CWho is client-to-server action 0x18 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

CWho has no payload. Darkages.exe:0x0043E384 net_c_send_who_request submits exactly one logical byte, the action.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0043E39Cnet_c_send_who_requestDarkages.exe:0x0043E384

UI flow

The E window button and mapped E/e key call ui_window_buttons_select, which sends CWho every time. On first use it also allocates, adds, and registers Users Dialog Pane, then hides it. SShowUsers 0x36 populates and shows that persistent pane when the reply arrives. See UI, Input, and Packet Flows.

CWhisper (0x19)

Previous: CWho | Client action index | Next: CUserSetting

CWhisper is client-to-server action 0x19 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004C1182sub_4C10D0Darkages.exe:0x004C10D0

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CUserSetting (0x1B)

Previous: CWhisper | Client action index | Next: CUse

CUserSetting is client-to-server action 0x1B in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001setting_codeOption selection code chosen by the active OptionPane-family control. Confirmed callers pass constants 0 through 8 or a control-derived byte.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00491AC4net_c_send_user_settingDarkages.exe:0x00491AA4

UI flow

Window button Q creates the dynamic OptionPane without sending a packet. Its controls and related option subdialogs call net_c_send_user_setting, which submits exactly [0x1B, setting_code]. The server can acknowledge a saved selection with SSelfSaveOk 0x21; the registered OptionPane then creates a local “Saved.” message dialog.

The individual meanings of setting codes 0 through 8 have not yet been assigned because the current evidence establishes control branches but not their server-side setting names.

See UI, Input, and Packet Flows.

CUse (0x1C)

Previous: CUserSetting | Client action index | Next: CEmotion

CUse is client-to-server action 0x1C in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004533C6sub_453394Darkages.exe:0x00453394

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CEmotion (0x1D)

Previous: CUse | Client action index | Next: CExitEditingMode

CEmotion is client-to-server action 0x1D in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00488694sub_488674Darkages.exe:0x00488674

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CExitEditingMode (0x23)

Previous: CEmotion | Client action index | Next: CDropGold

CExitEditingMode is client-to-server action 0x23 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001paper_modeMode byte stored at Paper object +0x554.
0x012text_lengthBig-endian number of following text bytes.
0x03text_lengthtextPaper text bytes without a terminator. Carriage returns are changed to tab bytes before submission.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00493C99ui_paper_dialog_handle_completionDarkages.exe:0x00493B60
Darkages.exe:0x0049421Cnet_c_send_exit_editing_modeDarkages.exe:0x00494100

UI flow

Both builders read the Paper text control, limit the local copy to 0x1F40 bytes, normalize carriage returns, and queue text_length + 4 logical bytes including the action. ui_paper_dialog_handle_completion performs this submission when its completion conditions allow it, then closes the Paper dialog. net_c_send_exit_editing_mode performs the same serialization without owning the close transition.

See UI, Input, and Packet Flows.

CDropGold (0x24)

Previous: CExitEditingMode | Client action index | Next: CChangePassword

CDropGold is client-to-server action 0x24 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004534E1sub_453424Darkages.exe:0x00453424

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CChangePassword (0x26)

Previous: CDropGold | Client action index | Next: CGive

CChangePassword is client-to-server action 0x26 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00463A45sub_4638C4Darkages.exe:0x004638C4
Darkages.exe:0x004642F0sub_464174Darkages.exe:0x00464174

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CGive (0x29)

Previous: CChangePassword | Client action index | Next: CGiveGold

CGive is client-to-server action 0x29 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0045336Asub_453324Darkages.exe:0x00453324

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CGiveGold (0x2A)

Previous: CGive | Client action index | Next: CSelfLook

CGiveGold is client-to-server action 0x2A in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0045360Bsub_453564Darkages.exe:0x00453564

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CSelfLook (0x2D)

Previous: CGiveGold | Client action index | Next: CGroup

CSelfLook is client-to-server action 0x2D in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

CSelfLook has no payload. Darkages.exe:0x0043D4C4 net_c_send_self_look submits exactly one logical byte, the action.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0043D4DCnet_c_send_self_lookDarkages.exe:0x0043D4C4

UI flow

The inventory/equipment selector sends this action only when the equipment pane is already the current content pane but has been hidden by selecting it twice. It sends CSelfLook before showing the pane again. Switching to equipment from another A/S/D/F/G content pane does not send it. SSelfLook 0x39 refreshes fields asynchronously and does not control visibility.

See UI, Input, and Packet Flows.

CGroup (0x2E)

Previous: CSelfLook | Client action index | Next: CGroupToggle

CGroup is client-to-server action 0x2E in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004306AEsub_430644Darkages.exe:0x00430644

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CGroupToggle (0x2F)

Previous: CGroup | Client action index | Next: CChangeSlot

CGroupToggle is client-to-server action 0x2F in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0042F1D0sub_42F1A4Darkages.exe:0x0042F1A4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CChangeSlot (0x30)

Previous: CGroupToggle | Client action index | Next: CRefreshUser

CChangeSlot is client-to-server action 0x30 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0042499Fsub_424914Darkages.exe:0x00424914
Darkages.exe:0x00441216sub_441164Darkages.exe:0x00441164
Darkages.exe:0x004427DFsub_442684Darkages.exe:0x00442684
Darkages.exe:0x0044395Fsub_443804Darkages.exe:0x00443804

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CRefreshUser (0x38)

Previous: CChangeSlot | Client action index | Next: CMenuCode

CRefreshUser is client-to-server action 0x38 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00488110sub_4880E4Darkages.exe:0x004880E4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CMenuCode (0x39)

Previous: CRefreshUser | Client action index | Next: CMessage

CMenuCode is client-to-server action 0x39 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001menu_typeServer-supplied menu or interaction type copied from the active menu pane.
0x014object_idBig-endian server object identifier copied from the active menu pane.
0x052menu_codeBig-endian selected menu code or control identifier.
0x07variablevariant_dataOptional selection byte, length-prefixed text, or class-specific values. Absent in the eight-byte logical form.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0047B8CDsub_47B850Darkages.exe:0x0047B850
Darkages.exe:0x0047BBB7sub_47BB00Darkages.exe:0x0047BB00
Darkages.exe:0x0047C175sub_47C100Darkages.exe:0x0047C100
Darkages.exe:0x0047C2CFsub_47C220Darkages.exe:0x0047C220
Darkages.exe:0x0047C60Fsub_47C560Darkages.exe:0x0047C560
Darkages.exe:0x0047C8C8sub_47C7C0Darkages.exe:0x0047C7C0
Darkages.exe:0x0047D3BFsub_47D310Darkages.exe:0x0047D310
Darkages.exe:0x0047D46Dsub_47D3E0Darkages.exe:0x0047D3E0
Darkages.exe:0x0047D7DCsub_47D6E4Darkages.exe:0x0047D6E4

UI flow

All nine traced builders share the seven-byte payload prefix above. They are called by controls owned by the dynamic SScreenMenu interaction. The simplest form submits eight logical bytes including the action. Other variants append a one-byte length and text, a selection byte, or three small class-specific values.

See UI, Input, and Packet Flows.

CMessage (0x3A)

Previous: CMenuCode | Client action index | Next: CBulletin

CMessage is client-to-server action 0x3A in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001dialog_typeType byte retained from the server-created message dialog.
0x014object_idBig-endian object identifier retained by the dialog.
0x052pursuit_idBig-endian server pursuit or interaction identifier.
0x072next_stepBig-endian stored step value plus one.
0x091response_typeValue 1 in the confirmed question-answer builders.
0x0A1answerSelected answer index passed to the dialog’s answer virtual.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0047E85Csub_47E7D0Darkages.exe:0x0047E7D0
Darkages.exe:0x0047F9ACsub_47F920Darkages.exe:0x0047F920
Darkages.exe:0x0047FA89sub_47F9C0Darkages.exe:0x0047F9C0
Darkages.exe:0x004805CCsub_480540Darkages.exe:0x00480540
Darkages.exe:0x004806A9sub_4805E0Darkages.exe:0x004805E0
Darkages.exe:0x0048113Csub_4810B0Darkages.exe:0x004810B0
Darkages.exe:0x004812AEsub_481150Darkages.exe:0x00481150
Darkages.exe:0x00481EECsub_481E60Darkages.exe:0x00481E60
Darkages.exe:0x0048205Bsub_481F00Darkages.exe:0x00481F00
Darkages.exe:0x00482B5Csub_482AD0Darkages.exe:0x00482AD0
Darkages.exe:0x00482F1Csub_482B70Darkages.exe:0x00482B70

Confirmed question answer form

ui_question_message_dialog_send_answer and ui_question_message_face_dialog_send_answer each submit 12 logical bytes including the action, exactly matching the fields above. These dialogs are created by SPursuitMenu subtypes 2 and 6. Other CMessage builders use related dialog variants and still require separate field comparison before their additional semantics are named.

See UI, Input, and Packet Flows.

CBulletin (0x3B)

Previous: CMessage | Client action index | Next: CPutToContainer

CBulletin is client-to-server action 0x3B in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

Every CBulletin payload begins with a one-byte command subtype.

SubtypePurposeConfirmed remaining payload
1Start bulletin sessionNone. The logical packet is [0x3B, 0x01].
2Request article or mail listTwo big-endian uint16_t values and one uint8_t. The concrete fields vary between board, article-list, and mail-list callers.
3Request one article or mailTwo big-endian uint16_t identifiers and one uint8_t.
4Post an articleBig-endian board identifier, one-byte title length and title, big-endian content length and content.
5Delete an article or mailTwo big-endian uint16_t identifiers, with an additional zero byte in two list-originated variants.
6Post mailBig-endian identifier, one-byte receiver length and receiver, one-byte title length and title, big-endian content length and content.
7Change article highlight stateTwo big-endian uint16_t identifiers.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0040ECE1net_c_send_bulletin_startDarkages.exe:0x0040ECA4
Darkages.exe:0x004109ECnet_c_send_bulletin_article_list_requestDarkages.exe:0x00410964
Darkages.exe:0x0041294Cnet_c_send_bulletin_mail_list_requestDarkages.exe:0x004128C4
Darkages.exe:0x004145B4net_c_send_bulletin_board_article_list_requestDarkages.exe:0x00414534
Darkages.exe:0x00415738net_c_send_bulletin_article_requestDarkages.exe:0x004156B4
Darkages.exe:0x00415934net_c_send_bulletin_article_highlightDarkages.exe:0x004158C4
Darkages.exe:0x0041715Cnet_c_send_bulletin_article_request_from_dialogDarkages.exe:0x004170D4
Darkages.exe:0x0041817Enet_c_send_bulletin_post_articleDarkages.exe:0x00417F94
Darkages.exe:0x004187E8net_c_send_bulletin_mail_requestDarkages.exe:0x00418764
Darkages.exe:0x0041A73Cnet_c_send_bulletin_mail_request_from_dialogDarkages.exe:0x0041A6B4
Darkages.exe:0x0041AE45net_c_send_bulletin_post_mailDarkages.exe:0x0041AB84
Darkages.exe:0x0041B4E4net_c_send_bulletin_delete_article_from_listDarkages.exe:0x0041B474
Darkages.exe:0x0041B60Bnet_c_send_bulletin_delete_articleDarkages.exe:0x0041B5A4
Darkages.exe:0x0041B8A8net_c_send_bulletin_delete_mail_from_listDarkages.exe:0x0041B824
Darkages.exe:0x0041BA1Bnet_c_send_bulletin_delete_mailDarkages.exe:0x0041B9B4

UI flow

Window button W creates BulletinSession and sends subtype 1. SBulletin subtype 1 creates BoardListDialog. Navigation, view, new, reply, delete, and highlight controls emit the later commands while the session remains registered. Server subtypes 1 through 5 replace the session’s active child with the matching board, article, or mail dialog.

The command field widths above are established at all 15 send sites. Some identifier meanings differ between board and mail contexts and remain unnamed until their server-side correspondence is confirmed.

See UI, Input, and Packet Flows.

CPutToContainer (0x3C)

Previous: CBulletin | Client action index | Next: CGetFromContainer

CPutToContainer is client-to-server action 0x3C in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00424B24sub_424A74Darkages.exe:0x00424A74

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CGetFromContainer (0x3D)

Previous: CPutToContainer | Client action index | Next: CUseSkill

CGetFromContainer is client-to-server action 0x3D in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004412D8sub_441234Darkages.exe:0x00441234
Darkages.exe:0x00453A15sub_4539B4Darkages.exe:0x004539B4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CUseSkill (0x3E)

Previous: CGetFromContainer | Client action index | Next: CFieldMap

CUseSkill is client-to-server action 0x3E in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001skill_slotSlot byte read from the activated skill pane at object +0x276.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00454C96net_c_send_use_skillDarkages.exe:0x00454C64

UI flow

Darkages.exe:0x00454C64 net_c_send_use_skill builds exactly [0x3E, skill_slot]. The slot activation path can first send a length-prefixed CSpellDelaySay 0x4E phrase loaded for the skill, then sends CUseSkill. Right-button down is separate and opens the local SkillBookDialog without network traffic.

See UI, Input, and Packet Flows.

CFieldMap (0x3F)

Previous: CUseSkill | Client action index | Next: CGetParcel

CFieldMap is client-to-server action 0x3F in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0043953Asub_4394B4Darkages.exe:0x004394B4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CGetParcel (0x41)

Previous: CFieldMap | Client action index | Next: CException

CGetParcel is client-to-server action 0x41 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00494E2Csub_494E14Darkages.exe:0x00494E14

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CException (0x42)

Previous: CGetParcel | Client action index | Next: CRequestObjectInfo

CException is client-to-server action 0x42 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00434B43sub_434A40Darkages.exe:0x00434A40

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CRequestObjectInfo (0x43)

Previous: CException | Client action index | Next: CRemoveEquip

CRequestObjectInfo is client-to-server action 0x43 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0047B755sub_47B704Darkages.exe:0x0047B704
Darkages.exe:0x00488723sub_4886A0Darkages.exe:0x004886A0
Darkages.exe:0x0048C820sub_48C774Darkages.exe:0x0048C774
Darkages.exe:0x0048F17Fsub_48F114Darkages.exe:0x0048F114

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CRemoveEquip (0x44)

Previous: CRequestObjectInfo | Client action index | Next: CReplyCRC

CRemoveEquip is client-to-server action 0x44 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0042F8E2sub_42F8A4Darkages.exe:0x0042F8A4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CReplyCRC (0x45)

Previous: CRemoveEquip | Client action index | Next: CGroupView

CReplyCRC is client-to-server action 0x45 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00465522sub_4654E4Darkages.exe:0x004654E4

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CGroupView (0x46)

Previous: CReplyCRC | Client action index | Next: CAddStat

CGroupView is client-to-server action 0x46 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0042F919sub_42F8F0Darkages.exe:0x0042F8F0

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CAddStat (0x47)

Previous: CGroupView | Client action index | Next: CRequestPatch

CAddStat is client-to-server action 0x47 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0044028Esub_440234Darkages.exe:0x00440234

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CRequestPatch (0x48)

Previous: CAddStat | Client action index | Next: CExchange

CRequestPatch is client-to-server action 0x48 in the 4.21 protocol.

Direction: client to server

Encrypted: No. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0048763Csub_487604Darkages.exe:0x00487604
Darkages.exe:0x004953ECsub_495380Darkages.exe:0x00495380
Darkages.exe:0x00495F55sub_495E80Darkages.exe:0x00495E80
Darkages.exe:0x0049609Dsub_496054Darkages.exe:0x00496054

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CExchange (0x4A)

Previous: CRequestPatch | Client action index | Next: CSpellDelayRequest

CExchange is client-to-server action 0x4A in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0043071Asub_4306C4Darkages.exe:0x004306C4
Darkages.exe:0x0043596Esub_435904Darkages.exe:0x00435904
Darkages.exe:0x00435A5Asub_435A04Darkages.exe:0x00435A04
Darkages.exe:0x00435ACAsub_435A74Darkages.exe:0x00435A74
Darkages.exe:0x00435E90sub_435E24Darkages.exe:0x00435E24
Darkages.exe:0x00437310sub_4372A4Darkages.exe:0x004372A4
Darkages.exe:0x00437AE8sub_437A64Darkages.exe:0x00437A64

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

CSpellDelayRequest (0x4D)

Previous: CExchange | Client action index | Next: CSpellDelaySay

CSpellDelayRequest is client-to-server action 0x4D in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001delay_secondsConfigured number of one-second delay steps before the held spell packet is submitted.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x00455CDFnet_c_send_spell_delay_requestDarkages.exe:0x00455CA4

Delay flow

Darkages.exe:0x00455CA4 net_c_send_spell_delay_request submits exactly [0x4D, delay_seconds]. It is called after the final CUseSpell logical packet has already been copied into the spell-delay controller. Timed CSpellDelaySay packets follow, and the controller releases the held cast on the final tick.

See UI, Input, and Packet Flows.

CSpellDelaySay (0x4E)

Previous: CSpellDelayRequest | Client action index | Next: CSendPortrait

CSpellDelaySay is client-to-server action 0x4E in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001text_lengthNumber of following phrase bytes, truncated to one byte.
0x01text_lengthtextDelay phrase bytes copied without a terminator.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004548F1net_c_send_skill_delay_sayDarkages.exe:0x00454884
Darkages.exe:0x00455921net_c_send_spell_delay_sayDarkages.exe:0x00455834
Darkages.exe:0x00455815ui_spell_delay_beginDarkages.exe:0x004556F4
Darkages.exe:0x00456997ui_spell_delay_handle_timerDarkages.exe:0x00456940

Delay flow

Both the skill and spell paths build CSpellDelaySay as action 0x4E, a one-byte length, and the selected phrase. Spell delay phrases are loaded from SpellBook.cfg and emitted once per timer step before the held CUseSpell packet is released. The skill slot can emit its configured phrase immediately before CUseSkill.

See UI, Input, and Packet Flows.

CSendPortrait (0x4F)

Previous: CSpellDelaySay | Client action index | Next: CMulti

CSendPortrait is client-to-server action 0x4F in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x0040A7E3net_c_build_portrait_responseDarkages.exe:0x0040A664

Schema status

The action purpose is independently established: net_c_build_portrait_response validates a local portrait and sends it after server action 0x49. Individual payload fields remain to be mapped.

CMulti (0x57)

Previous: CSendPortrait | Client action index | Next: baram

CMulti is client-to-server action 0x57 in the 4.21 protocol.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004A29D7sub_4A2994Darkages.exe:0x004A2994
Darkages.exe:0x004A2EB0sub_4A2E54Darkages.exe:0x004A2E54

Schema status

The 4.21 client emits this action at the listed sites. The payload fields and client-side trigger still require tracing.

baram (0x62)

Previous: CMulti | Client action index

baram is the literal bootstrap packet whose first byte is client action 0x62.

Direction: client to server

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x004bootstrap_textLiteral ASCII aram, completing baram.

Queue call sites

Queue callContaining IDA functionFunction address
Darkages.exe:0x004AD50Bsub_4AD140Darkages.exe:0x004AD140

Schema status

The complete five-byte submitted value is established. On initial process startup it carries sequence 0x00 and advances net_c_send_sequence to 0x01. Any reset or synchronization effect in the server is not visible in this client.

Server Actions

This index records every fixed action accepted by at least one pane’s socket-event handler. Each class name links to a dedicated page containing its payload format, handler functions, and current schema status. Message-page offsets are payload-relative and exclude framing, action, sequence, and the trailing zero.

The S... class names identify the 59 fixed server-to-client actions accepted by the traced 4.21 handlers. No additional fixed server action was found.

All entries use the server-to-client direction. Actions 0x00, 0x03, and 0x40 bypass the receive sequence and XOR decoder. Other entries are decoded before the Event type 9 packet is dispatched through pane virtual slot +0x40.

Fixed action index

ActionClass nameEncrypted4.21 handlers and notes
0x00SVersionCheckNoDarkages.exe:0x0045F780 ui_main_menu_handle_server_packet. Subtype zero reads the server version, selects table function 0 through 9, requires a nine-byte key, and queues both transformation updates. XOR bypass. Used during login or lobby.
0x01SNewUserCheckYesDarkages.exe:0x00461080 sub_461080. Used during login or lobby.
0x02SLoginCheckYesDarkages.exe:0x00461080 sub_461080, Darkages.exe:0x0045F780 ui_main_menu_handle_server_packet, Darkages.exe:0x00464300 sub_464300, Darkages.exe:0x00463A60 sub_463A60. Used during login or lobby.
0x03STransferServerNoDarkages.exe:0x0045F780 ui_main_menu_handle_server_packet and Darkages.exe:0x00468A90 ui_map_dispatch_server_packet. The MapPane branch calls transfer-server processing and can send kClientTransferServer. XOR bypass.
0x04SUserPositionYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet, Darkages.exe:0x004889F0 ui_local_user_handle_server_packet.
0x05SUserAppearanceYesDarkages.exe:0x004889F0 ui_local_user_handle_server_packet.
0x06SMapYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x07SDrawObjectsYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x08SStatusYesDarkages.exe:0x0043F420 sub_43F420, Darkages.exe:0x00441850 sub_441850, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet, Darkages.exe:0x004889F0 ui_local_user_handle_server_packet, Darkages.exe:0x004927F0 sub_4927F0, Darkages.exe:0x00494C60 sub_494C60.
0x0ASMessageYesDarkages.exe:0x0041CCA0 sub_41CCA0, Darkages.exe:0x00444690 sub_444690, Darkages.exe:0x0045F780 ui_main_menu_handle_server_packet, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet, Darkages.exe:0x00491520 sub_491520.
0x0BSMoveYesDarkages.exe:0x004889F0 ui_local_user_handle_server_packet.
0x0CSMoveObjectYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x0DSSayYesDarkages.exe:0x0041CCA0 sub_41CCA0, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x0ESRemoveObjectsYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet. The mapped branch removes an object from map state.
0x0FSAddItemYesDarkages.exe:0x00441850 sub_441850, Darkages.exe:0x004889F0 ui_local_user_handle_server_packet.
0x10SRemoveItemYesDarkages.exe:0x00441850 sub_441850, Darkages.exe:0x004889F0 ui_local_user_handle_server_packet.
0x11SChangeDirectionYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet, which calls Darkages.exe:0x0046B574 ui_map_handle_object_direction. Offset 0x01 is a big-endian object ID and offset 0x05 is a direction value from 0 through 3.
0x13SDamageEffectYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x15SMapInfoYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x17SAddSpellYesDarkages.exe:0x00443B10 ui_spell_inventory_handle_server_packet, Darkages.exe:0x004889F0 ui_local_user_handle_server_packet.
0x18SRemoveSpellYesDarkages.exe:0x00443B10 ui_spell_inventory_handle_server_packet, Darkages.exe:0x004889F0 ui_local_user_handle_server_packet.
0x19SSoundEffectYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x1ASMotionYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x1BSEnterEditingModeYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet. Used for paper editing.
0x1FSChangeWeatherYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x20SChangeHourYesDarkages.exe:0x0043F420 sub_43F420, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x21SSelfSaveOkYesDarkages.exe:0x004909E0 ui_option_pane_handle_server_packet.
0x26SActionChangeYesDarkages.exe:0x004234E0 sub_4234E0.
0x29SEffectLayerYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x2ASAddContainerYesDarkages.exe:0x00425370 sub_425370, Darkages.exe:0x00441850 sub_441850.
0x2BSRemoveContainerYesDarkages.exe:0x00425370 sub_425370.
0x2CSAddSkillYesDarkages.exe:0x00442990 ui_skill_inventory_handle_server_packet.
0x2DSRemoveSkillYesDarkages.exe:0x00442990 ui_skill_inventory_handle_server_packet.
0x2ESFieldMapYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x2FSScreenMenuYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet, Darkages.exe:0x00479480 sub_479480, Darkages.exe:0x0047AFD0 sub_47AFD0.
0x30SPursuitMenuYesDarkages.exe:0x00461080 sub_461080, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet, Darkages.exe:0x00479480 sub_479480.
0x31SBulletinYesDarkages.exe:0x00413CE0 ui_bulletin_session_handle_server_packet, Darkages.exe:0x00414260 ui_bulletin_dialog_handle_server_packet, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x32SStateObjectStateYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet. The mapped branch performs inline tile or object updates.
0x33SDrawHumanObjectYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x34SObjectInfoYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x35SShowPaperYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x36SShowUsersYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x37SAddEquipYesDarkages.exe:0x0042EAF0 ui_equip_pane_handle_server_packet.
0x38SRemoveEquipYesDarkages.exe:0x0042EAF0 ui_equip_pane_handle_server_packet.
0x39SSelfLookYesDarkages.exe:0x0042EAF0 ui_equip_pane_handle_server_packet.
0x3ASSpelledYesDarkages.exe:0x0043E8B0 sub_43E8B0.
0x3BSRequestCRCYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x3CSMapPartYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet.
0x3DSLevelPointYesDarkages.exe:0x0043F420 sub_43F420.
0x3ESWindowChangeYesDarkages.exe:0x0043D650 sub_43D650.
0x3FSActionCoolTimeYesDarkages.exe:0x00442990 ui_skill_inventory_handle_server_packet, Darkages.exe:0x00443B10 ui_spell_inventory_handle_server_packet.
0x40SSendPatchNoDarkages.exe:0x004955C0 sub_4955C0. XOR bypass. May be used during login or lobby.
0x42SExchangeYesDarkages.exe:0x00435EA0 sub_435EA0, Darkages.exe:0x004374F0 sub_4374F0, Darkages.exe:0x00437C70 sub_437C70, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet. Exchange-session handlers dispatch on a second byte with established subtypes 1 through 5.
0x48SSpellDelayCancelYesDarkages.exe:0x004568B0 sub_4568B0.
0x49SRequestPortraitYesDarkages.exe:0x0040B3A0 ui_handle_server_request_portrait. An embedded diagnostic names kServerRequestPortrait and states decimal value 73. The handler initiates client action 0x4F.
0x4BSBounceYesDarkages.exe:0x00468A90 ui_map_dispatch_server_packet, which calls Darkages.exe:0x0046CA54 net_forward_embedded_client_packet. Offset 0x01 is a big-endian embedded length and offset 0x03 begins the logical client packet.
0x4CSReconnectYesDarkages.exe:0x004921F0 ui_exit_wait_handle_server_packet. The Stone handler consumes all 0x4C packets. Subtype 0x01 sends CQuit subtype 0x00 and changes an exit-wait pane to the safe-exit message; it does not call the Socket reconnect path.
0x51SBlockInputYesDarkages.exe:0x004889F0 ui_local_user_handle_server_packet.
0x56SMultiYesDarkages.exe:0x004A2ED0 ui_server_select_handle_server_packet. ServerSelectDialogPane reads a count, allocates replacement entry storage, and deserializes fixed-width integers and colon-delimited strings before loading the table.

MapPane switch coverage

Darkages.exe:0x00468A90 accepts these 31 values:

03 04 06 07 08 0A 0C 0D 0E 11 13 15 19 1A 1B 1F
20 29 2E 2F 30 31 32 33 34 35 36 3B 3C 42 4B

Within its explicit 0x03 through 0x4B switch range, the other values return unhandled. A value can still be supported by another pane, as shown in the full index above.

SVersionCheck (0x00)

Server action index | Next: SNewUserCheck

SVersionCheck is server-to-client action 0x00 in the 4.21 protocol.

Direction: server to client

Encrypted: No. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001subtype0x00 negotiates transformation state, 0x01 reports an outdated client, and 0x02 enters the patch path.
0x011server_versionSubtype 0x00: stored and compared with the active server version.
0x021table_functionSubtype 0x00: queues table function 0 through 9.
0x031key_lengthSubtype 0x00: required to equal 9 by the main-menu handler.
0x04key_lengthkeySubtype 0x00: replacement repeating XOR key.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0045F780ui_main_menu_handle_server_packetAccepts the action in the main-menu packet handler.
Darkages.exe:0x004A3700net_queue_xor_table_functionQueues the subtype-zero table-function selector as Socket work code 11.
Darkages.exe:0x004A36C0net_queue_xor_keyCopies and queues the subtype-zero key as Socket work code 10.

Handler notes

The subtype-zero branch reads the server version, queues the one-byte table selector, requires a nine-byte key, and queues that key for the socket worker. Action 0x00 bypasses the sequence and XOR decoder, so negotiation does not depend on the previous transformation state.

Schema status

The subtype-zero payload prefix is established through the field readers and both queue calls. The data consumed by subtypes 0x01 and 0x02 still requires separate tracing.

SNewUserCheck (0x01)

Previous: SVersionCheck | Server action index | Next: SLoginCheck

SNewUserCheck is server-to-client action 0x01 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00461080sub_461080Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x00461080 sub_461080. Used during login or lobby.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SLoginCheck (0x02)

Previous: SNewUserCheck | Server action index | Next: STransferServer

SLoginCheck is server-to-client action 0x02 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00461080sub_461080Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x0045F780ui_main_menu_handle_server_packetAccepts the action in the main-menu packet handler.
Darkages.exe:0x00464300sub_464300Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00463A60sub_463A60Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x00461080 sub_461080, Darkages.exe:0x0045F780 ui_main_menu_handle_server_packet, Darkages.exe:0x00464300 sub_464300, Darkages.exe:0x00463A60 sub_463A60. Used during login or lobby.

Schema status

The 4.21 client accepts this action in the listed functions. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

STransferServer (0x03)

Previous: SLoginCheck | Server action index | Next: SUserPosition

STransferServer is server-to-client action 0x03 in the 4.21 protocol.

Direction: server to client

Encrypted: No. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0045F780ui_main_menu_handle_server_packetAccepts the action in the main-menu packet handler.
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x0045F780 ui_main_menu_handle_server_packet and Darkages.exe:0x00468A90 ui_map_dispatch_server_packet. The MapPane branch calls transfer-server processing and can send kClientTransferServer. XOR bypass.

Schema status

The 4.21 client accepts this action in the listed functions. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SUserPosition (0x04)

Previous: STransferServer | Server action index | Next: SUserAppearance

SUserPosition is server-to-client action 0x04 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.
Darkages.exe:0x004889F0ui_local_user_handle_server_packetAccepts the action in the local-user Pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet, Darkages.exe:0x004889F0 ui_local_user_handle_server_packet.

Schema status

The 4.21 client accepts this action in the listed functions. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SUserAppearance (0x05)

Previous: SUserPosition | Server action index | Next: SMap

SUserAppearance is server-to-client action 0x05 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x004889F0ui_local_user_handle_server_packetAccepts the action in the local-user Pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x004889F0 ui_local_user_handle_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SMap (0x06)

Previous: SUserAppearance | Server action index | Next: SDrawObjects

SMap is server-to-client action 0x06 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SDrawObjects (0x07)

Previous: SMap | Server action index | Next: SStatus

SDrawObjects is server-to-client action 0x07 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SStatus (0x08)

Previous: SDrawObjects | Server action index | Next: SMessage

SStatus is server-to-client action 0x08 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0043F420sub_43F420Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00441850sub_441850Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.
Darkages.exe:0x004889F0ui_local_user_handle_server_packetAccepts the action in the local-user Pane’s Event type 9 packet handler.
Darkages.exe:0x004927F0sub_4927F0Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00494C60sub_494C60Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x0043F420 sub_43F420, Darkages.exe:0x00441850 sub_441850, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet, Darkages.exe:0x004889F0 ui_local_user_handle_server_packet, Darkages.exe:0x004927F0 sub_4927F0, Darkages.exe:0x00494C60 sub_494C60.

The action is observed by several already registered game panes because status fields are distributed across more than one UI owner.

UI behavior

SStatus updates fields and redraw state but does not select or show the persistent status content pane. Status visibility is a client-only A/S/D/F/G transition. G or the status game button calls ui_game_buttons_select_status, hides the previous content pane, and shows the already populated status pane.

See UI, Input, and Packet Flows.

SMessage (0x0A)

Previous: SStatus | Server action index | Next: SMove

SMessage is server-to-client action 0x0A in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0041CCA0sub_41CCA0Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00444690sub_444690Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x0045F780ui_main_menu_handle_server_packetAccepts the action in the main-menu packet handler.
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.
Darkages.exe:0x00491520sub_491520Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x0041CCA0 sub_41CCA0, Darkages.exe:0x00444690 sub_444690, Darkages.exe:0x0045F780 ui_main_menu_handle_server_packet, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet, Darkages.exe:0x00491520 sub_491520.

In game, MapPane handles only message subtypes 8, 9, and 10 in this branch. It reads the big-endian text length at action-relative packet +2, changes tab bytes to carriage returns, allocates a dynamic message dialog, and registers it below BackgroundPane. Subtype 10 selects the alternate constructor mode; 8 and 9 use mode 0.

UI behavior

These subtypes create a new dialog rather than updating a static root. Other SMessage subtypes are not consumed by this MapPane branch and must not be assumed to share the same dialog format.

See UI, Input, and Packet Flows.

SMove (0x0B)

Previous: SMessage | Server action index | Next: SMoveObject

SMove is server-to-client action 0x0B in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001directionValues 0 through 3 acknowledge ordinary directions. Value 4 selects the position-correction path.
0x012xBig-endian 16-bit map coordinate, sign-extended by the handler. Used when direction is 4.
0x032yBig-endian 16-bit map coordinate, sign-extended by the handler. Used when direction is 4.
0x054unknown_05Present between the mapped coordinate fields and final byte. This handler does not read it.
0x091unknown_09Read as an unsigned byte and discarded. It is not compared with the client’s movement sequence.
0x0Apayload_length - 0x0Aunknown_0AAny additional payload bytes; the mapped local-user handler does not read them.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x004889F0ui_local_user_handle_server_packetAccepts action 0x0B in the local-user Pane’s Event type 9 handler.
Darkages.exe:0x004897C4ui_local_user_handle_smoveParses the payload, updates movement timing, and applies acknowledgment or correction behavior.

Handler notes

ui_local_user_handle_server_packet dispatches action 0x0B to ui_local_user_handle_smove. The handler does not perform a decoded-size check before reading through payload offset 0x09; transport framing and upstream event construction are expected to provide the bytes.

For direction 0 through 3, the handler checks that the value matches the local user’s current direction, updates the lag indicator, and completes its movement acknowledgment path. For value 4, it compares x and y with local position fields. A mismatch enters the position-correction path before the same lag update. Other direction values trigger the client’s diagnostic assertion and invalid-direction logging path.

Timing is recorded before the direction branch. The handler copies the current client movement sequence from local user +0x6F28 to +0x6F29, then stores this unsigned difference at +0x6F34:

uint32_t latest_move_response_ms = timeGetTime() - last_move_send_tick;

It calls ui_lag_indicator_update_from_local_user, which averages the sample with the previously displayed value. The client does not associate an SMove with a retained per-sequence send timestamp. See Lag indicator and movement timing for the memory fields and color bands.

Schema status

The ten-byte payload prefix read or skipped by this handler is established. The meanings of bytes 0x05 through 0x09 remain unknown because the mapped local-user handler either skips or discards them. This handler alone does not establish that offset 0x09 is the final payload byte.

SMoveObject (0x0C)

Previous: SMove | Server action index | Next: SSay

SMoveObject is server-to-client action 0x0C in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SSay (0x0D)

Previous: SMoveObject | Server action index | Next: SRemoveObjects

SSay is server-to-client action 0x0D in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0041CCA0sub_41CCA0Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x0041CCA0 sub_41CCA0, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed functions. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SRemoveObjects (0x0E)

Previous: SSay | Server action index | Next: SAddItem

SRemoveObjects is server-to-client action 0x0E in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet. The mapped branch removes an object from map state.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SAddItem (0x0F)

Previous: SRemoveObjects | Server action index | Next: SRemoveItem

SAddItem is server-to-client action 0x0F in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00441850sub_441850Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x004889F0ui_local_user_handle_server_packetAccepts the action in the local-user Pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x00441850 sub_441850, Darkages.exe:0x004889F0 ui_local_user_handle_server_packet.

Schema status

The 4.21 client accepts this action in the listed functions. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SRemoveItem (0x10)

Previous: SAddItem | Server action index | Next: SChangeDirection

SRemoveItem is server-to-client action 0x10 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00441850sub_441850Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x004889F0ui_local_user_handle_server_packetAccepts the action in the local-user Pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x00441850 sub_441850, Darkages.exe:0x004889F0 ui_local_user_handle_server_packet.

Schema status

The 4.21 client accepts this action in the listed functions. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SChangeDirection (0x11)

Previous: SRemoveItem | Server action index | Next: SDamageEffect

SChangeDirection is server-to-client action 0x11 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x004object_idUnsigned big-endian object identifier.
0x041directionDirection value. The mapped handler applies values 0 through 3.
0x05payload_length - 5unknown_05Any additional payload bytes; the mapped handler does not read them.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.
Darkages.exe:0x0046B574ui_map_handle_object_directionReads the object ID and direction and updates the matching map object.

Handler notes

ui_map_dispatch_server_packet calls ui_map_handle_object_direction. The helper reads the object ID at payload offset 0x00 and the direction at payload offset 0x04.

Schema status

The handler reads object_id, locates the map object, and applies direction only when its value is from 0 through 3. This payload prefix is established directly from the 4.21 instructions.

SDamageEffect (0x13)

Previous: SChangeDirection | Server action index | Next: SMapInfo

SDamageEffect is server-to-client action 0x13 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SMapInfo (0x15)

Previous: SDamageEffect | Server action index | Next: SAddSpell

SMapInfo is server-to-client action 0x15 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SAddSpell (0x17)

Previous: SMapInfo | Server action index | Next: SRemoveSpell

SAddSpell is server-to-client action 0x17 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001slot1-based spell inventory slot, valid range 1 through 36.
0x012display_idBig-endian display identifier retained by the slot pane.
0x031spell_typeSigned byte in the established range 1 through 8; selects the client activation path.
0x041name_lengthNumber of following spell-name bytes.
0x05name_lengthnameSpell name bytes. There is no terminator in the packet; the client appends one.
0x05 + name_length1comment_lengthNumber of following comment bytes.
0x06 + name_lengthcomment_lengthcommentSpell comment bytes. There is no terminator in the packet; the client appends one.
0x06 + name_length + comment_length1unknown_06_plus_name_length_plus_comment_lengthTrailing byte retained at slot pane +0x1F9 and passed to SpellBookDialog.

The payload occupies 7 + name_length + comment_length bytes. The string encoding and trailing-byte meaning are not established by this parser. ui_spell_inventory_apply_add_packet explicitly checks the slot range but does not compare either string length with the remaining decoded packet size before copying. The downstream constructor asserts that each NUL-terminated string is shorter than 128 bytes and that spell_type is 1 through 8.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00443B10ui_spell_inventory_handle_server_packetAccepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00443BB4ui_spell_inventory_apply_add_packetParses the fields and replaces the indexed slot child.
Darkages.exe:0x00443E14ui_spell_inventory_remove_slot_paneRemoves and deletes an existing slot child.
Darkages.exe:0x00443F24ui_spell_inventory_create_slot_paneAllocates and registers a 0x214-byte slot pane.
Darkages.exe:0x00456C70ui_spell_slot_pane_ctorRetains the slot, display ID, type, strings, and trailing byte.
Darkages.exe:0x004889F0ui_local_user_handle_server_packetAccepts the action in the local-user Pane’s Event type 9 packet handler.

Handler notes

ui_spell_inventory_apply_add_packet parses the action-bearing decoded packet at offsets +1 onward, corresponding to the payload table above. It rejects a slot outside 1 through 36. For a valid slot, it removes any existing pane, allocates a 0x214-byte spell slot pane, and adds and registers it below the spell inventory owner. Darkages.exe:0x004889F0 also observes this action in its own registered pane context.

UI behavior

This action changes the spell inventory’s child panes but does not select or show the persistent spell inventory. The player selects that parent locally with D or its game button. Right-button up over a populated slot opens SpellBookDialog without another server request.

The parent owns a 36-entry pointer array and the child retains all packet-derived state at stable offsets. See Runtime UI Memory Map and UI, Input, and Packet Flows.

SRemoveSpell (0x18)

Previous: SAddSpell | Server action index | Next: SSoundEffect

SRemoveSpell is server-to-client action 0x18 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00443B10ui_spell_inventory_handle_server_packetAccepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x004889F0ui_local_user_handle_server_packetAccepts the action in the local-user Pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x00443B10 ui_spell_inventory_handle_server_packet routes this action to the spell-slot removal path. That path unregisters and removes the child, deletes it, and clears the indexed owner pointer. Darkages.exe:0x004889F0 also observes the action in its own pane context.

UI behavior

Removing a slot does not hide the parent spell inventory. See UI, Input, and Packet Flows.

SSoundEffect (0x19)

Previous: SRemoveSpell | Server action index | Next: SMotion

SSoundEffect is server-to-client action 0x19 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SMotion (0x1A)

Previous: SSoundEffect | Server action index | Next: SEnterEditingMode

SMotion is server-to-client action 0x1A in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SEnterEditingMode (0x1B)

Previous: SMotion | Server action index | Next: SChangeWeather

SEnterEditingMode is server-to-client action 0x1B in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet. Used for paper editing.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SChangeWeather (0x1F)

Previous: SEnterEditingMode | Server action index | Next: SChangeHour

SChangeWeather is server-to-client action 0x1F in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SChangeHour (0x20)

Previous: SChangeWeather | Server action index | Next: SSelfSaveOk

SChangeHour is server-to-client action 0x20 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0043F420sub_43F420Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x0043F420 sub_43F420, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed functions. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SSelfSaveOk (0x21)

Previous: SChangeHour | Server action index | Next: SActionChange

SSelfSaveOk is server-to-client action 0x21 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

This action has no payload fields read by the OptionPane handler.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x004909E0ui_option_pane_handle_server_packetAccepts the action in OptionPane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x004909E0 ui_option_pane_handle_server_packet compares the decoded action byte to 0x21. It does not read later bytes.

UI behavior

While OptionPane is registered, this action allocates and constructs a local message dialog containing “Saved.” The packet acknowledges an earlier option save such as CUserSetting 0x1B; it does not create OptionPane itself.

See UI, Input, and Packet Flows.

SActionChange (0x26)

Previous: SSelfSaveOk | Server action index | Next: SEffectLayer

SActionChange is server-to-client action 0x26 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x004234E0sub_4234E0Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x004234E0 sub_4234E0.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SEffectLayer (0x29)

Previous: SActionChange | Server action index | Next: SAddContainer

SEffectLayer is server-to-client action 0x29 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SAddContainer (0x2A)

Previous: SEffectLayer | Server action index | Next: SRemoveContainer

SAddContainer is server-to-client action 0x2A in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00425370sub_425370Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00441850sub_441850Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x00425370 sub_425370, Darkages.exe:0x00441850 sub_441850.

Schema status

The 4.21 client accepts this action in the listed functions. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SRemoveContainer (0x2B)

Previous: SAddContainer | Server action index | Next: SAddSkill

SRemoveContainer is server-to-client action 0x2B in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00425370sub_425370Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x00425370 sub_425370.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SAddSkill (0x2C)

Previous: SRemoveContainer | Server action index | Next: SRemoveSkill

SAddSkill is server-to-client action 0x2C in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001slot1-based skill inventory slot, valid range 1 through 36.
0x012display_idBig-endian display identifier retained by the slot pane.
0x031name_lengthNumber of following skill-name bytes.
0x04name_lengthnameSkill name bytes. There is no terminator in the packet; the client appends one.

The payload occupies 4 + name_length bytes. The string encoding is not established by this parser. ui_skill_inventory_apply_add_packet checks the slot range but does not compare name_length with the remaining decoded packet size before copying. The downstream slot constructor asserts that the resulting NUL-terminated name is shorter than 128 bytes.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00442990ui_skill_inventory_handle_server_packetAccepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00442A34ui_skill_inventory_apply_add_packetParses the fields and replaces the indexed slot child.
Darkages.exe:0x00442C04ui_skill_inventory_remove_slot_paneRemoves and deletes an existing slot child.
Darkages.exe:0x00442D14ui_skill_inventory_create_slot_paneAllocates and registers a 0x294-byte slot pane.
Darkages.exe:0x00456B90ui_skill_slot_pane_ctorRetains the display ID, name, and 1-based slot in the new child.

Handler notes

ui_skill_inventory_apply_add_packet parses the action-bearing decoded packet at offsets +1 through +4, corresponding to payload offsets 0x00 through 0x03. It rejects a slot outside 1 through 36. For a valid slot, it removes any existing pane, allocates a 0x294-byte skill slot pane, and adds and registers it below the skill inventory owner.

UI behavior

This action changes skill slot children but does not select or show the persistent skill inventory. The player selects that parent locally with S or its game button. Right-button down over a populated slot opens SkillBookDialog without another server request.

The parent owns a 36-entry pointer array and the child retains its packet-derived state at stable offsets. See Runtime UI Memory Map and UI, Input, and Packet Flows.

SRemoveSkill (0x2D)

Previous: SAddSkill | Server action index | Next: SFieldMap

SRemoveSkill is server-to-client action 0x2D in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00442990ui_skill_inventory_handle_server_packetAccepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x00442990 ui_skill_inventory_handle_server_packet routes this action to the skill-slot removal path. That path unregisters and removes the child, deletes it, and clears the indexed owner pointer.

UI behavior

Removing a slot does not hide the parent skill inventory. See UI, Input, and Packet Flows.

SFieldMap (0x2E)

Previous: SRemoveSkill | Server action index | Next: SScreenMenu

SFieldMap is server-to-client action 0x2E in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SScreenMenu (0x2F)

Previous: SFieldMap | Server action index | Next: SPursuitMenu

SScreenMenu is server-to-client action 0x2F in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.
Darkages.exe:0x00479480sub_479480Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x0047AFD0sub_47AFD0Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

ui_map_dispatch_server_packet calls Darkages.exe:0x00469474 ui_map_handle_screen_menu, which allocates a 0x104-byte full-screen pane and calls ui_screen_menu_pane_ctor. The constructor adds it below BackgroundPane, registers it, and parses the original action 0x2F packet. The handlers at 0x00479480 and 0x0047AFD0 can receive later menu packets while related panes remain registered.

UI response path

Selections and text controls in this menu family send CMenuCode 0x39. Every traced builder copies a server-supplied menu type, big-endian object identifier, and big-endian menu code before variant-specific data.

See UI, Input, and Packet Flows.

SPursuitMenu (0x30)

Previous: SScreenMenu | Server action index | Next: SBulletin

SPursuitMenu is server-to-client action 0x30 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001dialog_subtypeSelects one of seven dynamic message classes, values 0 through 6.
0x01variabledialog_dataSubtype-specific message, face, identifiers, choices, and text.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00461080sub_461080Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.
Darkages.exe:0x00479480sub_479480Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

ui_map_dispatch_server_packet calls Darkages.exe:0x0046C004 ui_map_handle_pursuit_menu. Subtype 2 constructs QuestionMessageDialog; subtype 6 constructs QuestionMessageFaceDialog. The other five variants are dynamically allocated and registered, but their friendly source class names are not yet established. The handlers at 0x00461080 and 0x00479480 also observe pursuit traffic in their registered pane contexts.

UI response path

The two confirmed question classes send selected answers through CMessage 0x3A. Their answer builders retain the dialog type, object identifier, pursuit identifier, and step from the server packet, append response type 1 and the selected answer byte, and submit 12 logical bytes.

See UI, Input, and Packet Flows.

SBulletin (0x31)

Previous: SPursuitMenu | Server action index | Next: SStateObjectState

SBulletin is server-to-client action 0x31 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001response_subtypeSelects board list 1, article list 2, article 3, mail list 4, mail 5, or acknowledgement 6.
0x01variableresponse_dataSubtype-specific board, article, mail, or completion data.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00413CE0ui_bulletin_session_handle_server_packetAccepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00414260ui_bulletin_dialog_handle_server_packetAccepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00413CE0 ui_bulletin_session_handle_server_packet, Darkages.exe:0x00414260 ui_bulletin_dialog_handle_server_packet, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

UI behavior

An existing BulletinSession claims the packet through its Event type 9 handler and replaces its active child according to the subtype. Board, article-list, article, mail-list, and mail responses construct the correspondingly named dialog. Subtype 6 is accepted without constructing a child. Article-list and mail-list responses require the session request-wait flag.

When no session exists, ui_map_handle_bulletin can construct a server-opened session from this packet. Packet byte +2 bit 0 suppresses that unsolicited creation path when set.

See UI, Input, and Packet Flows.

SStateObjectState (0x32)

Previous: SBulletin | Server action index | Next: SDrawHumanObject

SStateObjectState is server-to-client action 0x32 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet. The mapped branch performs inline tile or object updates.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SDrawHumanObject (0x33)

Previous: SStateObjectState | Server action index | Next: SObjectInfo

SDrawHumanObject is server-to-client action 0x33 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SObjectInfo (0x34)

Previous: SDrawHumanObject | Server action index | Next: SShowPaper

SObjectInfo is server-to-client action 0x34 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SShowPaper (0x35)

Previous: SObjectInfo | Server action index | Next: SShowUsers

SShowPaper is server-to-client action 0x35 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

ui_map_dispatch_server_packet calls Darkages.exe:0x0046C3F4 ui_map_handle_show_paper. The handler allocates a 0x560-byte Paper dialog and calls ui_paper_dialog_ctor in mode 1. Its parser copies the server text and flags, then ui_paper_dialog_build_content adds the dialog below BackgroundPane and registers it for Events.

UI flow

This action creates a new dynamic dialog. It does not reuse a static Paper root. SEnterEditingMode 0x1B uses the same class in constructor mode 0. Paper submission can send CExitEditingMode 0x23 with a mode byte, big-endian text length, and normalized text.

See UI, Input, and Packet Flows.

SShowUsers (0x36)

Previous: SShowPaper | Server action index | Next: SAddEquip

SShowUsers is server-to-client action 0x36 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x002unknown_00Big-endian list metadata retained by the pane.
0x022user_countBig-endian number of variable-length user records.
0x04variableusersRecords containing flags, icon or status values, a name up to 48 bytes, another byte, and a secondary string up to 24 bytes.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

ui_map_dispatch_server_packet calls Darkages.exe:0x0046C464 ui_map_handle_show_users. It obtains the static Users Dialog Pane, parses the list, then calls ui_users_dialog_show.

UI flow

The pane is created lazily and hidden when the player first presses E or its window button. That input sends CWho 0x18. SShowUsers is the step that populates row controls, sets the visible state, installs a 100 ms pane timer, and invalidates the pane region.

See UI, Input, and Packet Flows.

SAddEquip (0x37)

Previous: SShowUsers | Server action index | Next: SRemoveEquip

SAddEquip is server-to-client action 0x37 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001slotEquipment slot index.
0x012item_idBig-endian equipment icon or item identifier.
0x031name_lengthNumber of following name bytes.
0x04name_lengthnameEquipment display name bytes.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0042EAF0ui_equip_pane_handle_server_packetAccepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x0042EAF0 ui_equip_pane_handle_server_packet.

UI behavior

ui_equip_pane_add_item accepts normal slots 1 through 13. It stores the identifier at pane +0xB22 + 2 * slot and the name at +0xB3E + 0x80 * (slot - 1), then redraws. The identifier is native little-endian in memory after the packet’s big-endian value is decoded. This action does not show the equipment pane.

See Runtime UI Memory Map and UI, Input, and Packet Flows.

SRemoveEquip (0x38)

Previous: SAddEquip | Server action index | Next: SSelfLook

SRemoveEquip is server-to-client action 0x38 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001slotEquipment slot whose identifier and name are cleared.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0042EAF0ui_equip_pane_handle_server_packetAccepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x0042EAF0 ui_equip_pane_handle_server_packet.

UI behavior

ui_equip_pane_remove_item clears the slot identifier and first byte of its name, then redraws. It does not hide the equipment pane.

See UI, Input, and Packet Flows.

SSelfLook (0x39)

Previous: SRemoveEquip | Server action index | Next: SSpelled

SSelfLook is server-to-client action 0x39 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0042EAF0ui_equip_pane_handle_server_packetAccepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x0042EAF0 ui_equip_pane_handle_server_packet calls ui_equip_pane_apply_self_look. The parser replaces appearance bytes, several length-prefixed character strings, a big-endian 32-bit value, and embedded legend/detail state, then invalidates the pane.

UI behavior

This action refreshes the persistent User Equip object but does not show it. Reopening an already selected but hidden equipment pane sends CSelfLook 0x2D and immediately shows the existing object; this response updates it asynchronously.

The static root is ui_equip_pane at Darkages.exe:0x004E32A4, RVA 0x000E32A4.

See UI, Input, and Packet Flows.

SSpelled (0x3A)

Previous: SSelfLook | Server action index | Next: SRequestCRC

SSpelled is server-to-client action 0x3A in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0043E8B0sub_43E8B0Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x0043E8B0 sub_43E8B0.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SRequestCRC (0x3B)

Previous: SSpelled | Server action index | Next: SMapPart

SRequestCRC is server-to-client action 0x3B in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SMapPart (0x3C)

Previous: SRequestCRC | Server action index | Next: SLevelPoint

SMapPart is server-to-client action 0x3C in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00468A90 ui_map_dispatch_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SLevelPoint (0x3D)

Previous: SMapPart | Server action index | Next: SWindowChange

SLevelPoint is server-to-client action 0x3D in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0043F420sub_43F420Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x0043F420 sub_43F420.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SWindowChange (0x3E)

Previous: SLevelPoint | Server action index | Next: SActionCoolTime

SWindowChange is server-to-client action 0x3E in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0043D650sub_43D650Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x0043D650 sub_43D650.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SActionCoolTime (0x3F)

Previous: SWindowChange | Server action index | Next: SSendPatch

SActionCoolTime is server-to-client action 0x3F in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00442990ui_skill_inventory_handle_server_packetAccepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00443B10ui_spell_inventory_handle_server_packetAccepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x00442990 ui_skill_inventory_handle_server_packet, Darkages.exe:0x00443B10 ui_spell_inventory_handle_server_packet.

Schema status

The 4.21 client accepts this action in the listed functions. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SSendPatch (0x40)

Previous: SActionCoolTime | Server action index | Next: SExchange

SSendPatch is server-to-client action 0x40 in the 4.21 protocol.

Direction: server to client

Encrypted: No. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x004955C0sub_4955C0Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x004955C0 sub_4955C0. XOR bypass. May be used during login or lobby.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SExchange (0x42)

Previous: SSendPatch | Server action index | Next: SSpellDelayCancel

SExchange is server-to-client action 0x42 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001subtypeExchange operation. Values 1 through 5 have mapped Stone branches.
0x01payload_length - 1unknown_01Subtype-dependent payload bytes whose field boundaries remain to be mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00435EA0sub_435EA0Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x004374F0sub_4374F0Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00437C70sub_437C70Accepts the action in its pane’s Event type 9 packet handler.
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.

Handler notes

Darkages.exe:0x00435EA0 sub_435EA0, Darkages.exe:0x004374F0 sub_4374F0, Darkages.exe:0x00437C70 sub_437C70, Darkages.exe:0x00468A90 ui_map_dispatch_server_packet. Exchange-session handlers dispatch on a second byte with established subtypes 1 through 5.

Schema status

The 4.21 exchange-pane handlers establish the subtype byte and branches 1 through 5. Their individual payload schemas remain to be mapped.

SSpellDelayCancel (0x48)

Previous: SExchange | Server action index | Next: SRequestPortrait

SSpellDelayCancel is server-to-client action 0x48 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x004568B0sub_4568B0Accepts the action in its pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x004568B0 sub_4568B0.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SRequestPortrait (0x49)

Previous: SSpellDelayCancel | Server action index | Next: SBounce

SRequestPortrait is server-to-client action 0x49 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x0040B3A0ui_handle_server_request_portraitHandles the portrait request and starts client portrait serialization.

Handler notes

Darkages.exe:0x0040B3A0 ui_handle_server_request_portrait. An embedded diagnostic names kServerRequestPortrait and states decimal value 73. The handler initiates client action 0x4F.

Schema status

The 4.21 diagnostic kServerRequestPortrait identifies this action. The handler initiates client action 0x4F; any request payload remains to be mapped.

SBounce (0x4B)

Previous: SRequestPortrait | Server action index | Next: SReconnect

SBounce is server-to-client action 0x4B in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x002embedded_lengthUnsigned big-endian length of the embedded logical client packet.
0x02embedded_lengthembedded_client_packetBegins with a client-direction action byte and is submitted through net_c_queue_send.
0x02 + embedded_lengthvariableunknown_tailAny remaining outer payload bytes; their exact boundary is not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x00468A90ui_map_dispatch_server_packetAccepts and dispatches the action in MapPane.
Darkages.exe:0x0046CA54net_forward_embedded_client_packetReads the embedded length and submits the embedded client packet.

Handler notes

ui_map_dispatch_server_packet calls net_forward_embedded_client_packet. The helper reads the big-endian embedded length at payload offset 0x00; the embedded logical client packet begins at payload offset 0x02.

Schema status

The 4.21 handler establishes the embedded length and forwarding behavior. The server can therefore select a client action dynamically rather than using one of the fixed action constants found in client builders.

SReconnect (0x4C)

Previous: SBounce | Server action index | Next: SBlockInput

SReconnect is server-to-client action 0x4C in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x001subtypeValue 0x01 completes the Stone safe-exit exchange. Other values are consumed without the mapped side effects.
0x01payload_length - 1unknown_01Any additional payload bytes.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x004921F0ui_exit_wait_handle_server_packetConsumes 0x4C; subtype 0x01 completes the safe-exit exchange.
Darkages.exe:0x00492310ui_exit_wait_pane_ctorCreates the exit-wait pane, shows the warning, and sends CQuit subtype 0x01.

Handler notes

Darkages.exe:0x004921F0 ui_exit_wait_handle_server_packet. The Stone handler consumes all 0x4C packets. Subtype 0x01 sends CQuit subtype 0x00 and changes an exit-wait pane to the safe-exit message; it does not call the Socket reconnect path.

Schema status

SReconnect is the 4.21 class name. In the mapped exit-wait path, ui_exit_wait_handle_server_packet claims every 0x4C packet. Subtype 0x01 sets the pane completion flag, sends CQuit subtype 0x00, and replaces the warning with the safe-to-exit text. The paired ui_exit_wait_pane_ctor sends CQuit subtype 0x01 when the wait pane is created. No Socket connection or reconnect function is called by this handler.

SBlockInput (0x51)

Previous: SReconnect | Server action index | Next: SMulti

SBlockInput is server-to-client action 0x51 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x004889F0ui_local_user_handle_server_packetAccepts the action in the local-user Pane’s Event type 9 packet handler.

Handler notes

Darkages.exe:0x004889F0 ui_local_user_handle_server_packet.

Schema status

The 4.21 client accepts this action in the listed function. Payload field division remains a placeholder until its readers and client-side effects are traced end to end.

SMulti (0x56)

Previous: SBlockInput | Server action index

SMulti is server-to-client action 0x56 in the 4.21 protocol.

Direction: server to client

Encrypted: Yes. See Sequence and XOR Transformation.

Payload offsets begin with the first byte after the action. The frame marker, frame length, action, sequence, and trailing zero are excluded.

Payload format

OffsetWidthFieldEstablished meaning
0x00payload_lengthunknown_00Payload bytes whose field boundaries are not yet mapped.

Handler functions

Function addressCurrent IDA nameRole
Darkages.exe:0x004A2ED0ui_server_select_handle_server_packetDeserializes the server-selection table.

Handler notes

Darkages.exe:0x004A2ED0 ui_server_select_handle_server_packet. ServerSelectDialogPane reads a count, allocates replacement entry storage, and deserializes fixed-width integers and colon-delimited strings before loading the table.

Schema status

The 4.21 client replaces and deserializes the server-selection table for this action. The exact packet fields will be documented after the reader helpers are traced end to end.

Analyzed Binary

Reproducible findings depend on identifying the exact input. The current local sample is not stored in this repository.

PropertyValueSource
Project labelDark Ages 4.21 Stone clientsupplied identification
FilenameDarkages.exelocal sample
Size1,126,476 byteslocal sample measurement
SHA-25636093dc1572521ea8c4c5f25548068c971a3d41c7722ef9fe0c05c94e24d6979local sample measurement
Windows file version360, 0, 0, 0Windows version resource
Windows product version360, 0, 0, 0Windows version resource

Open identity question

The working identification is 4.21 Stone client, while the executable’s Windows version resource reports 360, 0, 0, 0. The reason for this mismatch is unknown. Until another internal version marker, release artifact, or independently identified sample resolves it, references to 4.21 describe the project’s supplied identification rather than a value proven from the executable itself.

Reproducing the hash

On PowerShell:

Get-FileHash -Algorithm SHA256 -LiteralPath 'C:\path\to\Darkages.exe'

Do not place the executable in this repository.

Function Map

This appendix is a cross-subsystem index of important functions, grouped by primary subsystem. Cross-subsystem functions appear under the subsystem that owns their main control flow. Detailed decisions belong in the relevant subsystem chapter. An address remains the durable identifier when a name improves later.

Application lifecycle

AddressCurrent IDA nameSubsystemPrototypePurposeDetailed page
Darkages.exe:0x0041D390app_check_conflicting_softwareApplicationint __cdecl(void)Prompt for four process suffixes and two files before startup.Startup and Shutdown
Darkages.exe:0x0041D690win_top_level_window_existsWin32BOOL __cdecl(LPCSTR class_name, LPCSTR window_title)Test for a matching top-level window.Startup and Shutdown
Darkages.exe:0x0041D6C0app_check_runservices_entriesApplicationint __cdecl(void)Examine and optionally delete three RunServices values.Startup and Shutdown
Darkages.exe:0x0045B5F0app_win_mainApplication and Win32int __stdcall(HINSTANCE, HINSTANCE, LPSTR, int)Own class registration, checks, window creation, the message loop, and normal shutdown.Startup and Shutdown
Darkages.exe:0x0045B8F0app_shutdownApplicationvoid __cdecl(void)Delete the initialized subsystem graph in fixed order.Startup and Shutdown
Darkages.exe:0x0045C840app_set_working_directory_from_command_lineApplicationvoid __cdecl(void)Parse the executable directory from the raw command line and make it current.Startup and Shutdown
Darkages.exe:0x0045CCA0app_initializeApplicationvoid __cdecl(void)Construct resources, services, screen state, and event workers.Startup and Shutdown
Darkages.exe:0x00498430app_virus_check_module_callbackApplication and Win32int __cdecl(void *, void *, const char *, FILE *)Post the first virus-check module report to the main window.Input and Windows Events
Darkages.exe:0x00498780app_virus_check_threadApplicationvoid __cdecl(char *log_filename)Run the process and module virus checker on a worker thread.Input and Windows Events
Darkages.exe:0x0049D790ui_screen_pane_activateUI and Win32void __thiscall(void *screen_pane)Restore active screen and presentation state.Startup and Shutdown
Darkages.exe:0x004C62A5app_crt_entryApplication and CRTvoid __cdecl(void)Initialize the Microsoft C runtime and call app_win_main.Startup and Shutdown

Events

AddressCurrent IDA nameSubsystemPrototypePurposeDetailed page
Darkages.exe:0x0040E4D0event_deferred_delete_queue_ctorEventsvoid *__thiscall(void *queue_object)Construct the deferred-deletion queue.Internal Event Routing
Darkages.exe:0x0040E5C0event_deferred_delete_queue_drainEventsvoid __thiscall(void *queue_object)Delete and remove every deferred object.Internal Event Routing
Darkages.exe:0x00430F84event_dispatcher_dtorEvents and timingvoid __thiscall(void *event_dispatcher_object)Release dispatcher containers and balance multimedia timing state.Internal Event Routing
Darkages.exe:0x00430FE0event_dispatcher_ctorEvents and timingvoid *__thiscall(void *event_dispatcher_object)Construct the dispatcher, timer list, and worker timing state.Internal Event Routing
Darkages.exe:0x00431150event_dispatcher_register_paneEventsvoid __thiscall(void *event_dispatcher_object, void *pane, int hierarchy, int position)Register a pane and hierarchy metadata.Internal Event Routing
Darkages.exe:0x004311B0event_dispatcher_unregister_paneEventsvoid __thiscall(void *event_dispatcher_object, void *pane)Remove a pane from dispatcher traversal.Internal Event Routing
Darkages.exe:0x00431200event_dispatcher_queue_event_copyEventsvoid __thiscall(void *event_dispatcher_object, const void *event)Shallow-copy one 0x24-byte Event and queue dispatcher work code 3.Hooks and Injection
Darkages.exe:0x00431320event_dispatcher_insert_timerEvents and timingvoid __thiscall(void *event_dispatcher_object, void *receiver, int callback_id, unsigned int delay_ms, int payload_0, int payload_1)Insert a timer into the absolute-deadline sorted list.Internal Event Routing
Darkages.exe:0x00431470event_dispatcher_remove_pane_timersEvents and timingvoid __thiscall(void *event_dispatcher_object, void *receiver)Cancel every timer for one pane.Internal Event Routing
Darkages.exe:0x004315B0event_dispatcher_tickEvents and timingvoid __thiscall(void *event_dispatcher_object)Drain deferred deletion and dispatch at most one due timer.Internal Event Routing
Darkages.exe:0x004316E0event_dispatcher_process_work_itemEventsvoid __thiscall(void *event_dispatcher_object, int code, void *data, int value)Execute dispatcher work codes, including timer insert and cancellation.Internal Event Routing
Darkages.exe:0x00431B84event_dispatchEventsint __thiscall(void *this, void *event)Dispatch one internal Event.Internal Event Routing
Darkages.exe:0x00431D54event_dispatch_hierarchyEventsint __thiscall(void *this, void *event, void *hierarchy)Walk panes and call the type-specific virtual handler.Internal Event Routing
Darkages.exe:0x00432514event_manager_dtorEvents and networkvoid __thiscall(void *event_manager_object)Destroy EventMan and its owned Socket and clear both static roots.Internal Event Routing
Darkages.exe:0x00432630event_manager_ctorEvents and networkvoid *__thiscall(void *event_manager_object)Construct and publish the EventMan singleton.Internal Event Routing
Darkages.exe:0x00432BD0event_queue_mouse_moveEvents and inputvoid __thiscall(void *, int, int, unsigned int)Queue copied mouse coordinates and message time as work code 4.Input and Windows Events
Darkages.exe:0x00432C50event_queue_left_button_downEvents and inputvoid __thiscall(void *, unsigned int)Set left-button state and queue work code 5.Input and Windows Events
Darkages.exe:0x00432C90event_queue_left_button_upEvents and inputvoid __thiscall(void *, unsigned int)Clear left-button state and queue work code 6.Input and Windows Events
Darkages.exe:0x00432CC0event_queue_right_button_downEvents and inputvoid __thiscall(void *, unsigned int)Set right-button state and queue work code 7.Input and Windows Events
Darkages.exe:0x00432D00event_queue_right_button_upEvents and inputvoid __thiscall(void *, unsigned int)Clear right-button state and queue work code 8.Input and Windows Events
Darkages.exe:0x00432D30event_queue_mouse_wheelEvents and inputvoid __thiscall(void *, int, unsigned int)Queue wheel delta and message time as work code 9.Input and Windows Events
Darkages.exe:0x00432D60event_queue_key_downEvents and inputvoid __thiscall(void *, unsigned char, unsigned int)Queue a physical key press as work code 0x0A.Input and Windows Events
Darkages.exe:0x00432D90event_queue_key_upEvents and inputvoid __thiscall(void *, unsigned char, unsigned int)Queue a physical key release as work code 0x0B.Input and Windows Events
Darkages.exe:0x00432E50event_post_socket_bytesEvents and networkvoid __thiscall(void *this, const uint8_t *packet, int length)Copy decoded packet bytes to work code 0x0E.Internal Event Routing
Darkages.exe:0x00432F10event_manager_queue_event_copyEventsvoid __thiscall(void *event_manager_object, const void *event)Shallow-copy one 0x24-byte Event and queue EventMan work code 0x0F.Hooks and Injection
Darkages.exe:0x00433060event_manager_get_instanceEventsvoid *__cdecl(void)Assert and return the EventMan singleton.Input and Windows Events
Darkages.exe:0x00433110event_process_work_itemEventsvoid __thiscall(void *this, int code, void *data, int value)Convert event-manager worker records to Event objects.Internal Event Routing
Darkages.exe:0x00433434event_dispatch_mouse_moveEvents and inputvoid __thiscall(void *, int, int, unsigned int)Dispatch mouse subtype 0.Input and Windows Events
Darkages.exe:0x00433504event_dispatch_left_button_downEvents and inputvoid __thiscall(void *, unsigned int)Dispatch left single- or double-button-down input.Input and Windows Events
Darkages.exe:0x004336C4event_dispatch_left_button_upEvents and inputvoid __thiscall(void *, unsigned int)Dispatch left-button release.Input and Windows Events
Darkages.exe:0x00433794event_dispatch_right_button_downEvents and inputvoid __thiscall(void *, unsigned int)Dispatch right single- or double-button-down input.Input and Windows Events
Darkages.exe:0x00433944event_dispatch_right_button_upEvents and inputvoid __thiscall(void *, unsigned int)Dispatch right-button release.Input and Windows Events
Darkages.exe:0x00433A14event_dispatch_mouse_wheelEvents and inputvoid __thiscall(void *, int, unsigned int)Divide the wheel delta by 120 and dispatch subtype 7.Input and Windows Events
Darkages.exe:0x00433AB4event_dispatch_key_downEvents and inputvoid __thiscall(void *, unsigned char, unsigned int)Update mapped key state and dispatch key subtype 8.Input and Windows Events
Darkages.exe:0x00433C34event_update_key_upEvents and inputvoid __thiscall(void *, unsigned char, unsigned int)Clear pressed and modifier state without pane dispatch.Input and Windows Events
Darkages.exe:0x00433DC4event_queue_socket_packetEvents and networkvoid __stdcall(uint8_t *packet, uint32_t size)Queue Event type 9 with packet ownership.Internal Event Routing
Darkages.exe:0x00434080event_manager_periodic_noopEventsvoid __thiscall(void *event_manager_object)Native no-op EventMan periodic worker callback.Event Proxy Architecture
Darkages.exe:0x00470DA0util_memory_manager_deleting_dtorCross-subsystem memoryestablished scalar deleting destructorClear the MemoryMan root and optionally delete the object.Data Map
Darkages.exe:0x00470E10util_memory_manager_ctorCross-subsystem memoryvoid *__thiscall(void *memory_manager)Construct MemoryMan and publish its static root.Data Map
Darkages.exe:0x00470F30util_memory_manager_dtorCross-subsystem memoryvoid __thiscall(void *memory_manager)Destroy MemoryMan and clear its static root.Data Map
Darkages.exe:0x00470F80util_memory_manager_get_instanceCross-subsystem memoryvoid *__cdecl(void)Return the client MemoryMan singleton.Hooks and Injection
Darkages.exe:0x00470FC0util_memory_manager_allocCross-subsystem memoryvoid *__thiscall(void *memory_manager, int size)Allocate through the executable’s CRT wrapper.Hooks and Injection
Darkages.exe:0x004710D0util_memory_manager_freeCross-subsystem memoryint __thiscall(void *memory_manager, void *block)Free through the executable’s CRT wrapper.Internal Event Routing
Darkages.exe:0x00497EB0util_ring_buffer_ctorCross-subsystem workervoid *__thiscall(void *ring_buffer, int element_size, int capacity)Construct the monitor-protected bounded ring.Data Map
Darkages.exe:0x00498080util_ring_buffer_push_waitCross-subsystem workervoid __thiscall(void *ring_buffer, const void *element)Wait for capacity and append one record under the queue monitor.Internal Event Routing
Darkages.exe:0x004981B0util_ring_buffer_pop_waitCross-subsystem workervoid __thiscall(void *ring_buffer, void *element_out)Wait for data and remove one record under the queue monitor.Internal Event Routing
Darkages.exe:0x004BE9B0util_thread_queue_ctorCross-subsystem workervoid *__thiscall(void *worker_object, int queue_capacity)Construct queue state and a suspended worker thread.Internal Event Routing
Darkages.exe:0x004BEC34util_thread_queue_add_wait_handleCross-subsystem workervoid __thiscall(void *worker_object, void *wait_handle)Add one handle to the worker’s 16-entry wait set.Internal Event Routing
Darkages.exe:0x004BECB0util_thread_queue_dtorCross-subsystem workervoid __thiscall(void *worker_object)Terminate the worker and release its handles and queues.Internal Event Routing
Darkages.exe:0x004BEDF0util_thread_queue_set_wait_timeoutCross-subsystem workervoid __thiscall(void *worker_object, unsigned int timeout_ms)Set the WaitForMultipleObjects timeout.Internal Event Routing
Darkages.exe:0x004BEE00util_thread_queue_startCross-subsystem workervoid __thiscall(void *worker_object)Create the derived wait object and resume the worker.Internal Event Routing
Darkages.exe:0x004BF250util_thread_queue_worker_loopCross-subsystem workervoid __thiscall(void *worker_object)Wait for work or timeout and invoke derived worker methods.Internal Event Routing
Darkages.exe:0x004BF440util_thread_queue_post_asyncCross-subsystem workervoid __thiscall(void *worker_object, int code, void *data, int value)Append an asynchronous record and release the queue semaphore; admission can block on a full ring.Internal Event Routing

Networking

AddressCurrent IDA nameSubsystemPrototypePurposeDetailed page
Darkages.exe:0x0040A664net_c_build_portrait_responseNetwork and UIestablished __thiscall builderBuild client action 0x4F from the local portrait.Networking
Darkages.exe:0x0046CA54net_forward_embedded_client_packetNetworkestablished MapPane action handlerForward the client packet embedded in server action 0x4B.Client actions
Darkages.exe:0x004884E4net_c_send_moveNetwork, input, and timingvoid __thiscall(struct ui_local_user *, uint8_t)Queue CMove and start the local movement response timer.CMove
Darkages.exe:0x004A3294net_socket_dtorNetworkvoid __thiscall(void *socket_object)Close transports and tear down the Socket worker.Networking
Darkages.exe:0x004A32F0net_socket_ctorNetworkvoid *__thiscall(void *this, void *event_sink)Construct and initialize the Socket object.Networking
Darkages.exe:0x004A3570net_c_queue_sendNetworkvoid __thiscall(void *this, const uint8_t *packet, int16_t length)Copy, terminate, and queue a logical client packet.Networking
Darkages.exe:0x004A36C0net_queue_xor_keyNetworkvoid __thiscall(void *this, unsigned int key_length, const uint8_t *key)Copy and queue a negotiated XOR key.Sequence and XOR Transformation
Darkages.exe:0x004A3700net_queue_xor_table_functionNetworkvoid __thiscall(void *this, unsigned int function_index)Queue selection of table function 0 through 9.Sequence and XOR Transformation
Darkages.exe:0x004A39C0net_poll_receiveNetworkvoid __thiscall(void *socket_object)Poll receive state from the Socket periodic worker callback.Event Proxy Architecture
Darkages.exe:0x004A3A74net_s_receive_framesNetworkvoid __thiscall(void *this)Extract, decode, and post binary frames.Networking
Darkages.exe:0x004A44D4net_s_decode_packet_bodyNetworkint __stdcall(const uint8_t *src, int src_size, uint8_t *dest)Reverse the ordinary server-body XOR transformation.Sequence and XOR Transformation
Darkages.exe:0x004A4D34net_read_transport_byteNetworkint __thiscall(void *this, uint8_t *out_byte)Read a cached byte, refilling with recv or ReadFile.Networking
Darkages.exe:0x004A54D0net_process_work_itemNetworkvoid __thiscall(void *this, int code, void *data, int value)Execute queued Socket operations.Networking
Darkages.exe:0x004A59F4net_connect_configured_serverNetworkvoid __thiscall(void *this)Establish the configured socket and async notification.Networking
Darkages.exe:0x004A72B4net_c_send_packet_bodyNetworkvoid __thiscall(void *this, const uint8_t *packet, int16_t length)Transform, frame, and call send.Networking
Darkages.exe:0x004A79E4net_c_encode_packet_bodyNetworkint __stdcall(const uint8_t *src, int src_size, uint8_t *dest)Add sequence and apply ordinary client-body XOR passes.Sequence and XOR Transformation
Darkages.exe:0x004A8414net_set_xor_keyNetworkvoid __stdcall(int length, const uint8_t *key)Replace and repeat the runtime XOR key.Sequence and XOR Transformation
Darkages.exe:0x004A8590net_build_xor_tableNetworkvoid __stdcall(int function_index)Generate the 256-entry XOR table.Sequence and XOR Transformation

UI panes and registries

AddressCurrent IDA nameSubsystemPrototypePurposeDetailed page
Darkages.exe:0x0040A0A0ui_dialog_default_socket_handlerUI and eventsint __thiscall(void *, void *)Decline an unhandled Dialog socket Event.UI Panes and Registries
Darkages.exe:0x0040A0B0ui_dialog_default_timer_handlerUI and timingint __thiscall(void *, int, int, int)Decline an unhandled Dialog timer callback.UI Panes and Registries
Darkages.exe:0x0040A5A0ui_pane_default_key_handlerUI and eventsestablished Pane virtual handlerDefault keyboard handler at vslot +0x3C.UI Panes and Registries
Darkages.exe:0x0040A5B0ui_background_pane_ctorUIestablished __thiscall constructorConstruct the in-game BackgroundPane.UI Panes and Registries
Darkages.exe:0x0040B460ui_pane_default_mouse_handlerUI and eventsestablished Pane virtual handlerDefault mouse handler at vslot +0x38.UI Panes and Registries
Darkages.exe:0x0040B470ui_pane_default_socket_handlerUI and eventsestablished Pane virtual handlerDefault socket Event handler at vslot +0x40.UI Panes and Registries
Darkages.exe:0x0040E660ui_bulletin_session_deleting_dtorUIestablished __thiscall deleting destructorDestroy BulletinSession, its child history, and both registry memberships.Runtime UI Memory Map
Darkages.exe:0x0040EA50ui_pane_default_timer_handlerUI and timingestablished Pane virtual handlerDefault timer callback at vslot +0x44.UI Panes and Registries
Darkages.exe:0x0040EB10ui_bulletin_session_ctorUI and networkestablished __thiscall constructorConstruct and publish BulletinSession.UI, Input, and Packet Flows
Darkages.exe:0x0040ED04ui_bulletin_session_process_packetUI and networkestablished __thiscall methodDispatch SBulletin subtype to a replaceable child dialog.UI, Input, and Packet Flows
Darkages.exe:0x00414130ui_bulletin_dialog_handle_key_eventUI and eventsint __thiscall(void *, void *)Process shared Bulletin-family keyboard actions.UI Panes and Registries
Darkages.exe:0x00414260ui_bulletin_dialog_handle_server_packetUI and eventsint __thiscall(void *, void *)Claim server action 0x31 for Bulletin-family dialogs.UI Panes and Registries
Darkages.exe:0x004142E0ui_bulletin_dialog_handle_mouse_eventUI and eventsint __thiscall(void *, void *)Add Bulletin-family mouse capture and release behavior.UI Panes and Registries
Darkages.exe:0x00414BD0ui_article_list_dialog_start_new_article_editingUIestablished __thiscall methodStart new-article editing from ArticleListDialog.Pane Virtual Table Inventory
Darkages.exe:0x00415A10ui_article_list_dialog_handle_key_eventUI and eventsint __thiscall(void *, void *)Process ArticleListDialog keyboard actions.UI Panes and Registries
Darkages.exe:0x00416B20ui_article_list_pane_draw_cellUIestablished DrawCell methodDraw one ArticleListPane cell.Pane Virtual Table Inventory
Darkages.exe:0x004181A0ui_new_article_dialog_handle_key_eventUI and eventsint __thiscall(void *, void *)Process NewArticleDialog editing keys.UI Panes and Registries
Darkages.exe:0x004184C0ui_mail_list_dialog_handle_key_eventUI and eventsint __thiscall(void *, void *)Process MailListDialog keyboard actions.UI Panes and Registries
Darkages.exe:0x00419FE0ui_mail_list_pane_handle_selected_cell_double_clickUIestablished __thiscall methodOpen the selected mail from a list-cell double-click.Pane Virtual Table Inventory
Darkages.exe:0x0041A080ui_mail_list_pane_draw_cellUIestablished DrawCell methodDraw one MailListPane cell.Pane Virtual Table Inventory
Darkages.exe:0x0041AE70ui_new_mail_dialog_handle_key_eventUI and eventsint __thiscall(void *, void *)Process NewMailDialog editing keys.UI Panes and Registries
Darkages.exe:0x0041C4D0ui_legend_dialog_pane_ctorUIestablished __thiscall constructorConstruct LegendDialogPane and its inner list control.Pane Virtual Table Inventory
Darkages.exe:0x0041C770ui_legend_dialog_pane_handle_mouse_eventUI and eventsint __thiscall(void *, void *)Add LegendDialogPane left- and right-button behavior.UI Panes and Registries
Darkages.exe:0x00423984ui_lag_indicator_pane_dtorUIvoid __thiscall(struct ui_lag_indicator_pane *)Destroy the persistent lag indicator and clear its root.Runtime UI Memory Map
Darkages.exe:0x004239C0ui_get_lag_indicator_paneUIstruct ui_lag_indicator_pane *__cdecl(void)Return the persistent lag indicator pointer.Runtime UI Memory Map
Darkages.exe:0x004239D0ui_lag_indicator_pane_ctorUIstruct ui_lag_indicator_pane *__thiscall(struct ui_lag_indicator_pane *, void *)Construct and publish the lag indicator.Runtime UI Memory Map
Darkages.exe:0x00423A44ui_lag_indicator_pane_attachUIvoid __thiscall(struct ui_lag_indicator_pane *, void *)Add and register the lag indicator below BackgroundPane.Runtime UI Memory Map
Darkages.exe:0x00423AA0ui_lag_indicator_pane_drawUI and renderingvoid __thiscall(struct ui_lag_indicator_pane *)Select the lag indicator frame from the smoothed value.Runtime UI Memory Map
Darkages.exe:0x00423B60ui_lag_indicator_update_from_local_userUI and timingvoid __thiscall(struct ui_lag_indicator_pane *)Average the newest movement response sample and invalidate the Pane.Runtime UI Memory Map
Darkages.exe:0x0042A190ui_dialog_handle_mouse_eventUI and eventsint __thiscall(void *, void *)Process generic Dialog mouse subtypes.UI Panes and Registries
Darkages.exe:0x0042AED0ui_dialog_handle_key_eventUI and eventsint __thiscall(void *, void *)Process generic Dialog keys and focused controls.UI Panes and Registries
Darkages.exe:0x0042E1A0ui_equip_pane_ctorUIestablished __thiscall constructorConstruct and publish the persistent User Equip pane.UI, Input, and Packet Flows
Darkages.exe:0x0042EAF0ui_equip_pane_handle_server_packetUI and networkint __thiscall(void *, void *)Dispatch SAddEquip, SRemoveEquip, and SSelfLook.UI, Input, and Packet Flows
Darkages.exe:0x0042EBA4ui_equip_pane_apply_self_lookUI and character stateestablished __thiscall parserApply SSelfLook without changing visibility.UI, Input, and Packet Flows
Darkages.exe:0x0043A5F0ui_game_buttons_pane_ctorUIvoid *__thiscall(void *, void *, void *)Construct GameButtonsPane and its persistent content graph.Runtime UI Memory Map
Darkages.exe:0x0043CF20ui_game_buttons_handle_key_eventUI and inputint __thiscall(void *, void *)Map A/S/D/F/G to persistent content panes.UI, Input, and Packet Flows
Darkages.exe:0x0043D164ui_game_buttons_select_contentUIvoid __thiscall(void *, int8_t)Dispatch selection of equipment, skills, spells, chat, or status.UI, Input, and Packet Flows
Darkages.exe:0x0043D3D4ui_game_buttons_select_inventoryUI and networkvoid __thiscall(void *)Select or toggle equipment and request self-look on hidden reopen.UI, Input, and Packet Flows
Darkages.exe:0x0043E1B4ui_window_buttons_selectUI and networkvoid __thiscall(void *, int8_t)Dispatch Q/W/E window-button actions.UI, Input, and Packet Flows
Darkages.exe:0x0043E2B4ui_window_buttons_show_option_paneUIvoid __thiscall(void *)Allocate and construct dynamic OptionPane.UI, Input, and Packet Flows
Darkages.exe:0x0043E384net_c_send_who_requestNetwork and UIvoid __cdecl(void)Send one-byte CWho for Users Dialog Pane.CWho
Darkages.exe:0x0043E700ui_users_dialog_showUI and timingestablished __thiscall wrapperEnter the shared show body for the populated users pane.SShowUsers
Darkages.exe:0x00442A34ui_skill_inventory_apply_add_packetUI and networkint __thiscall(void *, const uint8_t *)Parse SAddSkill and replace one of 36 slot children.SAddSkill
Darkages.exe:0x00442C04ui_skill_inventory_remove_slot_paneUIvoid __thiscall(void *, uint8_t)Remove, unregister, delete, and clear an indexed skill pane.Runtime UI Memory Map
Darkages.exe:0x00442D14ui_skill_inventory_create_slot_paneUIvoid __thiscall(void *, uint8_t, uint16_t, const char *)Allocate and register an indexed skill pane.Runtime UI Memory Map
Darkages.exe:0x00443BB4ui_spell_inventory_apply_add_packetUI and networkint __thiscall(void *, const uint8_t *)Parse SAddSpell and replace one of 36 slot children.SAddSpell
Darkages.exe:0x00443E14ui_spell_inventory_remove_slot_paneUIvoid __thiscall(void *, uint8_t)Remove, unregister, delete, and clear an indexed spell pane.Runtime UI Memory Map
Darkages.exe:0x00443F24ui_spell_inventory_create_slot_paneUIvoid __thiscall(void *, uint8_t, uint16_t, int8_t, const char *, const char *, uint8_t)Allocate and register an indexed spell pane.Runtime UI Memory Map
Darkages.exe:0x00446090ui_point_initUIvoid __cdecl(void *, int, int)Initialize a two-coordinate client point.Runtime UI Memory Map
Darkages.exe:0x004460B0ui_rect_initUIvoid __cdecl(void *, int, int, int, int)Initialize a client rectangle while preserving its non-Win32 field order.Runtime UI Memory Map
Darkages.exe:0x00454CA0ui_skill_slot_finish_cooldownUI and timingestablished __thiscall methodClear a skill slot’s cooldown-active state.Runtime UI Memory Map
Darkages.exe:0x00454CC0ui_skill_slot_start_cooldownUI and timingestablished __thiscall methodSet a skill slot’s cooldown-active state.Runtime UI Memory Map
Darkages.exe:0x004544B0ui_skill_slot_handle_mouse_eventUI and inputint __thiscall(void *, void *)Open skill details or activate a skill slot.UI, Input, and Packet Flows
Darkages.exe:0x00455040ui_spell_slot_handle_mouse_eventUI and inputint __thiscall(void *, void *)Open spell details or enter a spell cast path.UI, Input, and Packet Flows
Darkages.exe:0x00455684ui_spell_slot_begin_castUI and networkvoid __thiscall(void *)Build the confirmed two-byte CUseSpell form.CUseSpell
Darkages.exe:0x004556F4ui_spell_delay_beginUI, network, and timingestablished __thiscall methodHold or immediately submit a built spell packet.UI, Input, and Packet Flows
Darkages.exe:0x00456220ui_spell_slot_finish_cooldownUI and timingestablished __thiscall methodClear a spell slot’s cooldown-active state.Runtime UI Memory Map
Darkages.exe:0x00456240ui_spell_slot_start_cooldownUI and timingestablished __thiscall methodSet a spell slot’s cooldown-active state.Runtime UI Memory Map
Darkages.exe:0x00456940ui_spell_delay_handle_timerUI, network, and timingint __thiscall(void *, int, int, int)Emit delay phrases and submit the held spell on the final tick.UI, Input, and Packet Flows
Darkages.exe:0x00456B90ui_skill_slot_pane_ctorUIvoid *__thiscall(void *, uint8_t, uint16_t, const char *)Initialize a persistent skill slot child.Runtime UI Memory Map
Darkages.exe:0x00456C70ui_spell_slot_pane_ctorUIvoid *__thiscall(void *, uint8_t, uint16_t, int8_t, const char *, const char *, uint8_t)Initialize a persistent spell slot child.Runtime UI Memory Map
Darkages.exe:0x0044E4D0ui_hierarchy_list_ctorUI and eventsvoid *__thiscall(void *, int)Construct a packed hierarchy list.UI Panes and Registries
Darkages.exe:0x004594A0ui_hierarchy_get_nodeUI and eventsvoid *__thiscall(void *, int)Return a checked packed-node address.UI Panes and Registries
Darkages.exe:0x0045D930ui_main_menu_pane_ctorUIestablished __thiscall constructorConstruct and publish MainMenuPane.UI Panes and Registries
Darkages.exe:0x0045FA44ui_main_menu_create_game_panesUIestablished __thiscall transition methodBuild and publish the in-game pane graph.UI Panes and Registries
Darkages.exe:0x004658C0ui_map_pane_ctorUIestablished __thiscall constructorConstruct the 0xF38-byte MapPane.UI Panes and Registries
Darkages.exe:0x00467320ui_map_handle_key_eventUI and eventsestablished Pane virtual handlerHandle MapPane keyboard Events.UI Panes and Registries
Darkages.exe:0x00467360ui_map_handle_mouse_eventUI and eventsestablished Pane virtual handlerHandle MapPane mouse Events.UI Panes and Registries
Darkages.exe:0x00469474ui_map_handle_screen_menuUI and networkestablished MapPane action handlerAllocate and construct an SScreenMenu pane.SScreenMenu
Darkages.exe:0x0046C004ui_map_handle_pursuit_menuUI and networkestablished MapPane action handlerAllocate one of seven SPursuitMenu dialog variants.SPursuitMenu
Darkages.exe:0x0046C3F4ui_map_handle_show_paperUI and networkestablished MapPane action handlerAllocate Paper in server-show mode.SShowPaper
Darkages.exe:0x0046C464ui_map_handle_show_usersUI and networkestablished MapPane action handlerPopulate and show Users Dialog Pane.SShowUsers
Darkages.exe:0x0046CAB4ui_map_handle_bulletinUI and networkestablished MapPane action handlerCreate a server-opened BulletinSession when allowed.SBulletin
Darkages.exe:0x00471D10ui_screen_menu_pane_ctorUI and networkestablished __thiscall constructorAdd, register, and parse a full-screen server menu.SScreenMenu
Darkages.exe:0x0047DBA0ui_question_message_dialog_handle_mouse_eventUI and eventsint __thiscall(void *, void *)Add drag behavior for the question-dialog family.UI Panes and Registries
Darkages.exe:0x0047EFC0ui_question_message_dialog_ctorUI and networkestablished __thiscall constructorConstruct SPursuitMenu subtype 2 question dialog.SPursuitMenu
Darkages.exe:0x0047F9C0ui_question_message_dialog_send_answerUI and networkestablished __thiscall methodSend the selected answer from QuestionMessageDialog.Pane Virtual Table Inventory
Darkages.exe:0x004805E0ui_question_message_face_dialog_send_answerUI and networkestablished __thiscall methodSend the selected answer from QuestionMessageFaceDialog.Pane Virtual Table Inventory
Darkages.exe:0x004862E0ui_monster_pane2_start_moveUI and world stateestablished __thiscall methodStart a MonsterPane2 movement transition.Pane Virtual Table Inventory
Darkages.exe:0x004876C4ui_local_user_dtorUI and world statevoid __thiscall(struct ui_local_user *)Destroy the local-user Pane and clear its class-level alias.Runtime UI Memory Map
Darkages.exe:0x00487700ui_local_user_ctorUI and world statestruct ui_local_user *__thiscall(struct ui_local_user *, void *)Construct and publish the active local-user Pane.Runtime UI Memory Map
Darkages.exe:0x00487860ui_get_local_userUI and world statestruct ui_local_user *__cdecl(void)Return the active local-user pointer.Runtime UI Memory Map
Darkages.exe:0x0048D2E0ui_effect_object_pane_handle_timerUI and timingestablished Pane timer handlerProcess EffectObjectPane timer callbacks.Pane Virtual Table Inventory
Darkages.exe:0x0048F600ui_option_pane_ctorUIvoid *__thiscall(void *)Build, add, and register dynamic OptionPane.UI, Input, and Packet Flows
Darkages.exe:0x004909E0ui_option_pane_handle_server_packetUI and networkint __thiscall(void *, void *)Claim SSelfSaveOk and create a confirmation dialog.SSelfSaveOk
Darkages.exe:0x00490A80ui_option_pane_handle_mouse_eventUI and inputint __thiscall(void *, void *)Handle OptionPane mouse state.Pane Virtual Table Inventory
Darkages.exe:0x00490D70ui_option_pane_handle_timerUI and timingint __thiscall(void *, int, int, int)Advance OptionPane timer state.Pane Virtual Table Inventory
Darkages.exe:0x00490FE0ui_option_pane_handle_key_eventUI and inputint __thiscall(void *, void *)Handle OptionPane keyboard shortcuts.Pane Virtual Table Inventory
Darkages.exe:0x00491AA4net_c_send_user_settingNetwork and UIvoid __thiscall(void *, uint8_t)Send CUserSetting from an option control.CUserSetting
Darkages.exe:0x00492A90ui_pane_dtorUIvoid __thiscall(void *)Cancel timers and destroy the Pane base.UI Panes and Registries
Darkages.exe:0x00492AD0ui_pane_ctorUIvoid *__thiscall(void *, unsigned char, unsigned char)Construct common Pane fields and install the base vtable.UI Panes and Registries
Darkages.exe:0x00492C10ui_pane_is_visibleUIint __thiscall(void *)Query common pane visible state.UI, Input, and Packet Flows
Darkages.exe:0x00492C40ui_pane_showUIvoid __thiscall(void *)Set visible state and invalidate the pane.UI Panes and Registries
Darkages.exe:0x00492D50ui_pane_hideUIvoid __thiscall(void *)Clear visible state and invalidate the exposed region.UI Panes and Registries
Darkages.exe:0x00493240ui_pane_get_bound_rectUIvoid __thiscall(void *, int32_t *)Translate a Pane’s local bounds by its relative origin.Runtime UI Memory Map
Darkages.exe:0x004932B0ui_pane_set_bound_rectUIvoid __thiscall(void *, const int32_t *)Split Pane bounds into relative origin and local rectangle.Runtime UI Memory Map
Darkages.exe:0x004933C0ui_pane_add_to_screenUIvoid __thiscall(void *, const int32_t *, void *, void *)Add a Pane to Screen composition using a client-order rectangle.UI Panes and Registries
Darkages.exe:0x00493480ui_pane_remove_from_screenUIvoid __thiscall(void *)Remove a Pane from Screen composition.UI Panes and Registries
Darkages.exe:0x00493530ui_pane_register_eventsUI and eventsvoid __thiscall(void *, void *, int)Register a Pane in event traversal.UI Panes and Registries
Darkages.exe:0x004935D0ui_pane_unregister_eventsUI and eventsvoid __thiscall(void *)Remove a Pane from event traversal.UI Panes and Registries
Darkages.exe:0x004939C0ui_paper_dialog_ctorUI and networkestablished __thiscall constructorParse and construct Paper in mode 0 or 1.UI, Input, and Packet Flows
Darkages.exe:0x00493B60ui_paper_dialog_handle_completionUI and networkint __thiscall(void *, int, int)Optionally submit Paper content, then close the dialog.CExitEditingMode
Darkages.exe:0x00494100net_c_send_exit_editing_modeNetwork and UIvoid __thiscall(void *)Serialize current Paper content as action 0x23.CExitEditingMode
Darkages.exe:0x00498F20ui_screen_registry_ctorUIvoid *__thiscall(void *)Construct the Screen composition hierarchy.UI Panes and Registries
Darkages.exe:0x00498F40ui_screen_add_paneUIestablished __thiscall methodValidate and insert a Screen node.UI Panes and Registries
Darkages.exe:0x004993C4ui_screen_find_pane_nodeUIestablished recursive __thiscall searchFind a pane’s packed Screen node.UI Panes and Registries
Darkages.exe:0x00499530ui_screen_remove_paneUIestablished __thiscall methodRemove a Screen node and child hierarchy.UI Panes and Registries
Darkages.exe:0x00499ED0ui_screen_get_pane_originUIestablished __thiscall queryAccumulate a pane’s screen-space origin.UI Panes and Registries
Darkages.exe:0x004A1680ui_scrollable_pane_handle_mouse_eventUI and eventsint __thiscall(void *, void *)Route mouse input through scroll bars and list content.UI Panes and Registries
Darkages.exe:0x004A1900ui_scrollable_pane_handle_key_eventUI and eventsint __thiscall(void *, void *)Route keyboard input through scroll bars and list content.UI Panes and Registries
Darkages.exe:0x004A1B04ui_server_select_dialog_pane_dtorUIestablished __thiscall destructorUnregister and remove ServerSelectDialogPane.UI Panes and Registries
Darkages.exe:0x004A2740ui_server_select_dialog_pane_ctorUIestablished __thiscall constructorConstruct, add, and register ServerSelectDialogPane.UI Panes and Registries
Darkages.exe:0x004AC944ui_terminal_pane_dtorUIestablished __thiscall destructorClear the terminal root and destroy derived state.UI Panes and Registries
Darkages.exe:0x004ACA00ui_terminal_pane_ctorUIestablished __thiscall constructorConstruct, publish, add, and register TerminalPane.UI Panes and Registries
Darkages.exe:0x004AD130ui_get_terminal_paneUIvoid *__cdecl(void)Return the static TerminalPane pointer.UI Panes and Registries
Darkages.exe:0x004BE6F4ui_text_edit_handle_key_eventUI and eventsestablished Pane virtual handlerComplete or cancel a temporary TextEdit pane.UI Panes and Registries
Darkages.exe:0x004C2D90ui_weather_pane_drawUI and renderingestablished Draw methodDraw WeatherPane cloud and weather state.Pane Virtual Table Inventory

UI protocol dispatch

AddressCurrent IDA nameSubsystemPrototypePurposeDetailed page
Darkages.exe:0x0040B3A0ui_handle_server_request_portraitNetwork and UIint __thiscall(void *this, void *event)Handle server action 0x49.Server actions
Darkages.exe:0x0045F780ui_main_menu_handle_server_packetNetwork and UIint __thiscall(void *this, void *event)Handle initial main-menu server packets.Server actions
Darkages.exe:0x00468A90ui_map_dispatch_server_packetNetwork and UIint __thiscall(void *this, void *event)Dispatch 31 in-game server actions.Server actions
Darkages.exe:0x0046B574ui_map_handle_object_directionNetwork and UIint __thiscall(void *this, const uint8_t *packet)Apply server action 0x11 to a map object’s direction.SChangeDirection
Darkages.exe:0x004889F0ui_local_user_handle_server_packetNetwork, UI, and world stateint __thiscall(struct ui_local_user *, void *)Dispatch the server actions owned by the local-user Pane.SMove
Darkages.exe:0x004897C4ui_local_user_handle_smoveNetwork, UI, and timingint __thiscall(struct ui_local_user *, const uint8_t *)Apply SMove and update movement response timing.SMove
Darkages.exe:0x004921F0ui_exit_wait_handle_server_packetNetwork and UIint __thiscall(void *this, void *event)Complete the subtype-one safe-exit exchange for server action 0x4C.SReconnect
Darkages.exe:0x00492310ui_exit_wait_pane_ctorUI and networkestablished __thiscall constructorCreate the exit-wait pane and send CQuit subtype 0x01.SReconnect
Darkages.exe:0x004A2ED0ui_server_select_handle_server_packetNetwork and UIint __thiscall(void *this, void *event)Deserialize the action 0x56 server table.Server actions

Input and Windows integration

AddressCurrent IDA nameSubsystemPrototypePurposeDetailed page
Darkages.exe:0x0045BDC0win_main_window_procWin32, UI, input, and networkLRESULT __stdcall(HWND, UINT, WPARAM, LPARAM)Route the 15 explicit DAClass messages and delegate all others.Input and Windows Events
Darkages.exe:0x0049DA30render_screen_pane_load_paletteRendering and Win32void __thiscall(void *, const char *)Load and apply the 256-entry screen palette.Input and Windows Events
Darkages.exe:0x0049E820ui_screen_pane_set_cursor_positionUI and inputvoid __thiscall(void *, int, int)Update the 32 by 32 software-cursor rectangle.Input and Windows Events

Entry requirements

Add a function when it is useful outside its immediate subsystem context. Record the calling convention and types established by the code, and link to the detailed subsystem explanation.

Data Map

This appendix tracks important globals, tables, structures, virtual tables, and persistent buffers. See Runtime UI Memory Map for process-relative pointer chains, Pane tree traversal, class-specific UI fields, and safe snapshot guidance. C Structure Layouts provides packed C99 views of the same established fields without replacing the detailed offset tables below.

AddressCurrent IDA nameType or sizeOwnerPurposeDetailed pageNotes
Darkages.exe:0x004D64C0app_window_activeuint8_tApplication and Win32Records whether the main window is treated as active.Startup and ShutdownSet before the message loop and by WM_ACTIVATEAPP handling.
Darkages.exe:0x004E0F00net_xor_tableuint32_t[256]Socket transformationGenerated XOR byte table.Sequence and XOR TransformationEach dword repeats one byte four times. Initial contents implement function 0.
Darkages.exe:0x004E2D70ui_bulletin_sessionvoid *UI and bulletin/mailStatic root of the active heap BulletinSession Pane.Runtime UI Memory MapTen child pointers begin at object +0xF8, current index is +0xF5, active child is +0x120, and the request-wait byte is +0x124. RVA 0x000E2D70.
Darkages.exe:0x004E2E40ui_lag_indicator_panevoid *, object 0xF8 bytesUI and timingStatic root of the persistent movement-latency indicator.Runtime UI Memory MapSmoothed millisecond value is at pane +0xF4. RVA 0x000E2E40.
Darkages.exe:0x004E32A4ui_equip_panevoid *UI and character stateStatic root of the persistent heap User Equip pane.Runtime UI Memory MapSlot identifier expression is +0xB22 + 2 * slot; names use +0xB3E + 0x80 * (slot - 1); embedded legend/details ownership is at +0x11CC. RVA 0x000E32A4.
Darkages.exe:0x004E32C0event_manager_instancevoid *Events and networkEventMan singleton that converts worker records into Event objects.Internal Event RoutingPublished by event_manager_ctor and started separately from event_dispatcher.
Darkages.exe:0x004E3564ui_users_dialog_panevoid *UIStatic root of the heap Users Dialog Pane.UI, Input, and Packet FlowsNull before the first CWho UI action creates the pane; reused and shown by later SShowUsers replies. RVA 0x000E3564.
Darkages.exe:0x004F5198ui_local_uservoid *, object 0x776C bytesUI, character, and timingOwning in-game root of the local user object.Runtime UI Memory MapMovement sequence and timing fields are at +0x6F28 through +0x6F34. RVA 0x000F5198.
Darkages.exe:0x004F51ACui_main_menu_panevoid *, object 0x124 bytesUICurrent heap MainMenuPane.UI Panes and RegistriesNon-null during the main-menu phase and cleared on transition or teardown. RVA 0x000F51AC.
Darkages.exe:0x004F51B0ui_map_panevoid *, object 0xF38 bytesUICurrent heap MapPane.UI Panes and RegistriesPublished after game-pane creation and cleared during game teardown. RVA 0x000F51B0.
Darkages.exe:0x004F51B4ui_background_panevoid *, object 0x144 bytesUICurrent heap BackgroundPane.UI Panes and RegistriesPublished with the game UI and cleared during game teardown. RVA 0x000F51B4.
Darkages.exe:0x004F51B8event_deferred_delete_queuevoid *EventsHolds objects awaiting deletion by the dispatcher tick.Internal Event RoutingCreated during initialization, drained from last element to first, and deleted after the dispatcher.
Darkages.exe:0x004F51BCnet_socket_instancevoid *Network and EventManStatic root of the active heap Socket object.IPC, Rules, and StateEventMan owns the Socket at object +0x68, publishes this root during construction, and clears it during destruction. RVA 0x000F51BC.
Darkages.exe:0x004F51C4app_configvoid *ApplicationGlobal configuration object.Startup and ShutdownConstructed from Darkages.cfg when English.nfo exists, otherwise from Legend.cfg.
Darkages.exe:0x004F51C8ui_screen_panevoid *UI and renderingGlobal 640 by 480, 8-bit screen pane.Startup and ShutdownRegistered with the event dispatcher and reactivated around the message loop.
Darkages.exe:0x004F51CCui_screen_registryvoid *UIHeap Screen composition hierarchy.UI Panes and RegistriesStride 0x3F; list fields are at +0x0C, +0x14, +0x18, and +0x1C. RVA 0x000F51CC.
Darkages.exe:0x004F51D0event_dispatchervoid *EventsGlobal event dispatcher and timer-worker object.Internal Event RoutingCreated and started by app_initialize; destroyed by app_shutdown.
Darkages.exe:0x004F51D4app_error_codeint32_tApplicationProcess-wide subsystem initialization error.Startup and ShutdownChecked after most constructors and before the message loop.
Darkages.exe:0x004F51DCapp_main_windowHWNDApplication and Win32Main DAClass window handle.Startup and ShutdownA null result from CreateWindowExA is stored but not rejected.
Darkages.exe:0x004F51E0app_instanceHINSTANCEApplication and Win32Saved process instance handle.Startup and ShutdownAssigned by app_win_main after class registration.
Darkages.exe:0x004F51F4app_shutdown_requesteduint8_tApplicationRequests termination of the blocking message loop.Startup and ShutdownObserved only after GetMessageA returns. Several writers also post a message to wake the loop.
Darkages.exe:0x004FA0E0app_single_instance_mutexHANDLEApplicationHandle for Nexon.SingleInstance.Startup and ShutdownClosed and cleared only after normal app_shutdown.
Darkages.exe:0x004FA160util_memory_manager_instancevoid *Cross-subsystem memoryStatic MemoryMan root used by client allocation and free wrappers.Hooks and InjectionRequired when an injected server packet will be released by dispatcher cleanup. RVA 0x000FA160.
Darkages.exe:0x004FC244ui_local_user_singletonvoid *UI and character stateClass-level alias of the active local user object.Runtime UI Memory MapSet by ui_local_user_ctor and cleared by ui_local_user_dtor. RVA 0x000FC244.
Darkages.exe:0x004FD320app_virus_check_failure_detailchar[256]Application checksDetail inserted into the virus-scan failure prompt.Input and Windows EventsUsed when WM_USER + 0x2046 arrives with zero wParam.
Darkages.exe:0x004FD420app_virus_report_modulechar[256]Application checksFirst infected-module name reported to the window thread.Input and Windows EventsIts address is posted in wParam for WM_USER + 0x2046.
Darkages.exe:0x004FD5A0net_xor_keykey storage, at least 13 bytesSocket transformationRuntime repeating XOR key.Sequence and XOR TransformationConstructor default is ASCII NexonInc.. Negotiated updates require at most 9 bytes.
Darkages.exe:0x004FD5ACnet_xor_key_lengthint32_tSocket transformationNumber of bytes in the active key.Sequence and XOR TransformationDefault is 9.
Darkages.exe:0x004FD5B0net_xor_key_repeateduint8_t[48]Socket transformationFour adjacent copies of the key.Sequence and XOR TransformationSupports optimized four-byte XOR loops.
Darkages.exe:0x004FD5E0net_c_send_sequenceuint8_tSocket transformationSequence inserted after ordinary transformed client actions.Sequence and XOR TransformationZero-initialized at process load and incremented modulo 256 only by the transformed client encoder; cleartext bypass actions do not consume it.
Darkages.exe:0x004FD640ui_terminal_panevoid *, object 0xF78 bytesUICurrent heap TerminalPane.UI Panes and RegistriesPublished by the constructor, returned by ui_get_terminal_pane, and cleared by the destructor. RVA 0x000FD640.
Darkages.exe:0x005002C0ui_aggrement_dialog_pane_vtablevirtual tableAggrementDialogPaneRuntime class discriminator for the agreement dialog.Pane Virtual Table InventoryAggrement is the spelling retained in the binary.
Darkages.exe:0x00500700ui_background_pane_vtablevirtual tableUIConcrete BackgroundPane handler table.UI Panes and RegistriesSocket slot +0x40 handles server portrait requests.
Darkages.exe:0x00501E60ui_bulletin_session_vtablevirtual tableBulletinSessionRuntime class discriminator for the bulletin and mail session Pane.Runtime UI Memory MapSocket slot +0x40 claims action 0x31. RVA 0x00101E60.
Darkages.exe:0x00501EC0ui_bulletin_dialog_pane_vtablevirtual tableBulletinDialogPaneRuntime class discriminator and Event handler table.Pane Virtual Table InventorySocket slot claims action 0x31.
Darkages.exe:0x00501F40ui_article_list_dialog_vtablevirtual tableArticleListDialogRuntime class discriminator and Event handler table.Pane Virtual Table InventoryCustom keyboard and editing handlers.
Darkages.exe:0x00501FC0ui_mail_list_dialog_vtablevirtual tableMailListDialogRuntime class discriminator and Event handler table.Pane Virtual Table InventoryCustom keyboard and completion handlers.
Darkages.exe:0x005020C0ui_new_article_dialog_vtablevirtual tableNewArticleDialogRuntime class discriminator and Event handler table.Pane Virtual Table InventoryControl-click handler is at vslot +0x4C.
Darkages.exe:0x00502140ui_new_mail_dialog_vtablevirtual tableNewMailDialogRuntime class discriminator and Event handler table.Pane Virtual Table InventoryControl-click handler is at vslot +0x4C.
Darkages.exe:0x005021C0ui_board_list_dialog_vtablevirtual tableBoardListDialogRuntime class discriminator and Event handler table.Pane Virtual Table InventoryHas a class-specific timer override.
Darkages.exe:0x00502240ui_article_dialog_vtablevirtual tableArticleDialogRuntime class discriminator and Event handler table.Pane Virtual Table InventoryBulletin-family mouse and socket handlers.
Darkages.exe:0x005022C0ui_mail_dialog_vtablevirtual tableMailDialogRuntime class discriminator and Event handler table.Pane Virtual Table InventoryBulletin-family mouse and socket handlers.
Darkages.exe:0x005023E0ui_article_list_pane_vtablevirtual tableArticleListPaneRuntime class discriminator for the scrollable article list.Pane Virtual Table InventoryDrawCell virtual is at +0x84.
Darkages.exe:0x00502580ui_mail_list_pane_vtablevirtual tableMailListPaneRuntime class discriminator for the scrollable mail list.Pane Virtual Table InventorySelection and DrawCell virtuals are at +0x80/+0x84.
Darkages.exe:0x00506C80ui_legend_dialog_pane_vtablevirtual tableLegendDialogPaneRuntime class discriminator for the outer character-legend dialog.Pane Virtual Table InventoryThe inner control vtable at 0x00506BE0 remains unnamed.
Darkages.exe:0x00509C20ui_lag_indicator_pane_vtablevirtual tablelag indicator paneRuntime discriminator for the persistent movement-latency indicator.Runtime UI Memory MapDraw vslot +0x48 selects one of four statcon.epf frames. RVA 0x00109C20.
Darkages.exe:0x0050C6C0ui_equip_pane_vtablevirtual tableUser Equip paneRuntime discriminator for the persistent equipment and self-look Pane.Runtime UI Memory MapRVA 0x0010C6C0.
Darkages.exe:0x0050D180event_dispatcher_vtablevoid *[8]EventsVirtual table for pane dispatch, work processing, wait handling, and periodic ticks.Internal Event RoutingSlot +0x10 points to event_dispatcher_tick.
Darkages.exe:0x0050D720event_manager_vtablevoid *[8]Events and networkEventMan worker and dispatch virtual table.Internal Event RoutingSlot +0x10 points to event_manager_periodic_noop; slot +0x14 reaches event_process_work_item.
Darkages.exe:0x0050D740event_vtablevoid *[2]EventsRuntime discriminator installed in copied mouse, keyboard, and socket Event objects.Hooks and InjectionEvent objects are 0x24 bytes in this build. RVA 0x0010D740.
Darkages.exe:0x00510300ui_game_buttons_pane_vtablevirtual tableGameButtonsPaneRuntime discriminator for the persistent in-game content owner.Runtime UI Memory MapRVA 0x00110300.
Darkages.exe:0x00510420ui_skill_inventory_pane_vtablevirtual tableskill inventory parentRuntime discriminator for the 36-entry skill inventory.Runtime UI Memory MapRVA 0x00110420.
Darkages.exe:0x00510480ui_spell_inventory_pane_vtablevirtual tablespell inventory parentRuntime discriminator for the 36-entry spell inventory.Runtime UI Memory MapRVA 0x00110480.
Darkages.exe:0x005106E0ui_users_dialog_pane_vtablevirtual tableUsers Dialog PaneRuntime discriminator for the reusable users dialog.Runtime UI Memory MapRVA 0x001106E0.
Darkages.exe:0x005134E0ui_human_image_control_pane_vtablevirtual tableHumanImageControlPaneRuntime class discriminator for the human image control.Pane Virtual Table InventoryDraw virtual is at +0x48.
Darkages.exe:0x00514820ui_skill_slot_pane_vtablevirtual tableskill slot childRuntime discriminator for a populated skill slot.Runtime UI Memory MapRVA 0x00114820.
Darkages.exe:0x00514880ui_spell_slot_pane_vtablevirtual tablespell slot childRuntime discriminator for a populated spell slot.Runtime UI Memory MapRVA 0x00114880.
Darkages.exe:0x005177C0ui_main_menu_pane_vtablevirtual tableMainMenuPaneIncludes mouse, keyboard, and server-packet handlers.UI Panes and RegistriesPacket handler is ui_main_menu_handle_server_packet.
Darkages.exe:0x00519280ui_map_pane_vtablevirtual tableMapPaneIncludes mouse, keyboard, timer, and in-game server handlers.UI Panes and RegistriesSocket Event virtual slot +0x40 points to ui_map_dispatch_server_packet.
Darkages.exe:0x0051B6C0ui_item_list_pane_vtablevirtual tableItemListPaneRuntime class discriminator for the scrollable item list.Pane Virtual Table InventoryDrawCell virtual is at +0x84.
Darkages.exe:0x0051BE60ui_monster_image_control_pane_vtablevirtual tableMonsterImageControlPaneRuntime class discriminator for the monster image control.Pane Virtual Table InventoryDraw virtual is at +0x48.
Darkages.exe:0x0051C0C0ui_question_message_dialog_vtablevirtual tableQuestionMessageDialogRuntime class discriminator for the question dialog.Pane Virtual Table InventoryAnswer sender is at class vslot +0x74.
Darkages.exe:0x0051C140ui_question_message_face_dialog_vtablevirtual tableQuestionMessageFaceDialogRuntime class discriminator for the faced question dialog.Pane Virtual Table InventoryUser-dialog and answer virtuals are at +0x64/+0x74.
Darkages.exe:0x0051D700ui_monster_pane2_vtablevirtual tableMonsterPane2Runtime class discriminator and Event handler table.Pane Virtual Table InventoryStartMove virtual is at +0x64.
Darkages.exe:0x0051E980ui_local_user_vtablevirtual tableNewUser local-user paneRuntime discriminator for the active local user object.Runtime UI Memory MapSocket vslot +0x40 dispatches SMove and the other local-user actions. RVA 0x0011E980.
Darkages.exe:0x0051FB00ui_effect_object_pane_vtablevirtual tableEffectObjectPaneRuntime class discriminator and Event handler table.Pane Virtual Table InventoryTimer vslot +0x44 is class-specific.
Darkages.exe:0x00520660ui_option_pane_vtablevirtual tableOptionPaneRuntime class discriminator for the main options dialog.Pane Virtual Table InventoryMouse, key, socket, and timer slots are all class-specific.
Darkages.exe:0x005207E0ui_exit_wait_pane_vtablevirtual tableexit-wait paneIncludes the server action 0x4C handler used by the orderly-exit exchange.SReconnectInstalled by ui_exit_wait_pane_ctor; the source-level C++ class name is not yet established.
Darkages.exe:0x005213A0ui_pane_vtablevirtual tableUIBase Pane lifecycle and event slots.UI Panes and RegistriesScreen slots are +0x28/+0x2C; event registration is +0x30/+0x34; handlers begin at +0x38.
Darkages.exe:0x00524CA0ui_screen_registry_vtablevirtual tableUIScreen composition registry table.UI Panes and RegistriesOwns packed 0x3F-byte Screen nodes.
Darkages.exe:0x00524CE0ui_screen_pane_vtablevirtual tableUI and renderingPersistent ScreenPane handler table.UI Panes and RegistriesKeyboard and timer slots have concrete overrides.
Darkages.exe:0x00526140ui_server_select_dialog_pane_vtablevirtual tableServerSelectDialogPaneIncludes the action 0x56 server-list handler.UI Panes and RegistriesConstructor and deletion diagnostics establish the class name.
Darkages.exe:0x00526940net_socket_vtablevirtual tableSocketSocket work-item interface.NetworkingSlot +0x10 points to net_poll_receive; slot +0x14 reaches net_process_work_item.
Darkages.exe:0x00528280ui_spell_book_dialog_vtablevirtual tableSpellBookDialogRuntime class discriminator for the spell-book dialog.Pane Virtual Table InventoryDraw virtual is at +0x48.
Darkages.exe:0x005282E0ui_skill_book_dialog_vtablevirtual tableSkillBookDialogRuntime class discriminator for the skill-book dialog.Pane Virtual Table InventoryDraw virtual is at +0x48.
Darkages.exe:0x00529000ui_terminal_pane_vtablevirtual tableTerminalPaneIncludes mouse, keyboard, and terminal server-packet handlers.UI Panes and RegistriesInstalled by ui_terminal_pane_ctor.
Darkages.exe:0x0052CFA0ui_weather_pane_vtablevirtual tableWeatherPaneRuntime class discriminator and Event handler table.Pane Virtual Table InventoryTimer and Draw virtuals are class-specific.

GameButtons content fields

These offsets are relative to the GameButtons owner found through the Screen or Event hierarchy. They connect persistent visible content to the owner even though the owner itself does not have a static root. The Runtime UI Memory Map records the full traversal and class-specific layout.

OffsetWidthWorking fieldReads and writes
+0x00F44map_paneConstructor-owned link that normally matches ui_map_pane.
+0x01001equipment_selectedUpdated with the other four button-state bytes during content selection.
+0x01081skill_selectedUpdated with the other four button-state bytes during content selection.
+0x01101spell_selectedUpdated with the other four button-state bytes during content selection.
+0x01181chat_selectedUpdated with the other four button-state bytes during content selection.
+0x01201status_selectedUpdated with the other four button-state bytes during content selection.
+0x01244current_content_paneCompared and replaced by all five content selectors.
+0x01304chat_paneSelected by F/f or content index 3.
+0x01344status_paneSelected by G/g or content index 4.
+0x01384equip_paneSelected by A/a or content index 0; same-pane selection can toggle visibility.
+0x013C4skill_inventory_paneSelected by S/s or content index 1.
+0x01404spell_inventory_paneSelected by D/d or content index 2.
+0x01444system_messageConstructor-owned link used by the GameButtons graph.

Worker queue and bounded ring layouts

The dispatcher, EventMan, and Socket begin with the same 0x68-byte worker base. These fields establish the in-process wake point and the native queue state relevant to an Event Proxy.

OffsetWidthWorking fieldReads and writes
+0x004vtableDerived worker vtable. Periodic virtual slot is +0x10.
+0x0C4wait_timeout_msINFINITE by default; dispatcher changes it to 1.
+0x101wait_handle_countNumber of entries beginning at +0x14.
+0x1464wait_handles[16]Entry 0 is the work semaphore released after a native queue push.
+0x544work_queuePointer to the synchronized bounded ring.
+0x584completion_monitorProtects synchronous waiter records.
+0x5C4completion_waitersList used only by synchronous work requests.
+0x604worker_threadThread handle created suspended and later resumed.
+0x644worker_thread_idThread identifier written by _beginthreadex.

Each native work record is 0x18 bytes. Asynchronous posting sets the final three fields to zero.

OffsetWidthWorking field
+0x004code
+0x044data
+0x084value
+0x0C4completion_event
+0x104result_buffer
+0x144result_buffer_size

The ring object pointed to by worker +0x54 has this established state:

OffsetWidthWorking field
+0x0C4monitor
+0x104not_full_condition
+0x144not_empty_condition
+0x184element_size
+0x1C4capacity
+0x204buffer
+0x244element_count
+0x284head_index
+0x2C4tail_index

Programmer-oriented layouts follow. They are documentation types, not recovered source declarations.

typedef uint32_t client_ptr32;

typedef struct worker_record_421 {
    uint32_t code;
    client_ptr32 data;
    int32_t value;
    client_ptr32 completion_event;
    client_ptr32 result_buffer;
    int32_t result_buffer_size;
} worker_record_421;

typedef struct worker_queue_base_421 {
    client_ptr32 vtable;
    uint8_t unknown_04[8];
    uint32_t wait_timeout_ms;
    uint8_t wait_handle_count;
    uint8_t unknown_11[3];
    client_ptr32 wait_handles[16];
    client_ptr32 work_queue;
    client_ptr32 completion_monitor;
    client_ptr32 completion_waiters;
    client_ptr32 worker_thread;
    uint32_t worker_thread_id;
} worker_queue_base_421;

typedef struct bounded_ring_421 {
    client_ptr32 vtable;
    uint8_t unknown_04[8];
    client_ptr32 monitor;
    client_ptr32 not_full_condition;
    client_ptr32 not_empty_condition;
    int32_t element_size;
    int32_t capacity;
    client_ptr32 buffer;
    int32_t element_count;
    int32_t head_index;
    int32_t tail_index;
} bounded_ring_421;

The Socket and EventMan constructors pass capacity 0x80; the dispatcher passes 0x400. An observer can sample element_count, but the value may change concurrently and external code should not write queue metadata. For a worker-affine proxy wake, validate the root and vtable, read wait_handles[0], and call ReleaseSemaphore without touching the native ring.

Event dispatcher fields

Offsets below are from the object stored in event_dispatcher. The earlier fields belong to its worker-queue base.

OffsetWidthWorking fieldReads and writes
+0x00004vtableSet to event_dispatcher_vtable.
+0x000C4wait_timeout_msSet to 1 and passed to WaitForMultipleObjects.
+0x00101wait_handle_countNumber of entries beginning at +0x14.
+0x0014variablewait_handlesHandle index 0 is the queued-work notification.
+0x00604worker_threadCreated suspended, resumed during initialization, and terminated by the base destructor.
+0x00684pane_registryRegistry traversed by internal event dispatch.
+0x006C4multimedia_period_msPassed to timeBeginPeriod and timeEndPeriod.
+0x00704current_tickLatest 32-bit timeGetTime sample and base for relative timer insertion.
+0x00744next_deadlineFirst timer deadline, or UINT32_MAX when no timer is pending.
+0x00784timer_listSorted list with capacity 256 and 20-byte records.

Event timer record

OffsetWidthMeaning
+0x004Receiver pane pointer.
+0x044Callback identifier.
+0x084Absolute 32-bit timeGetTime deadline.
+0x0C4First callback payload value.
+0x104Second callback payload value.

Event manager input fields

Offsets below are from the object stored in event_manager_instance. They are used by the window-message input queue and the EventMan worker.

OffsetWidthWorking fieldReads and writes
+0x006C4mouse_yUpdated from the first word of internal (y, x) mouse-move work and copied into button and wheel Events.
+0x00704mouse_xUpdated from the second word of internal (y, x) mouse-move work and copied into button and wheel Events.
+0x007C4double_click_time_limitA second button down is eligible only when its unsigned elapsed message time is strictly less than this value.
+0x00801previous_click_buttonValue 1 records a prior left down; value 0 records a prior right down.
+0x00844previous_click_yFirst coordinate used by internal double-click recognition.
+0x00884previous_click_xSecond coordinate used by internal double-click recognition.
+0x008C4previous_click_timeGetMessageTime value for the prior button-down candidate.
+0x0390256scan_code_pressedPer-scan-code state; bit 7 is set on key down and cleared on key up.
+0x04901input_modifier_stateCombined mouse-button and mapped keyboard modifier bits placed in Events.
+0x04B04input_block_flagsBit 0 suppresses movement, wheel, button-down, and key-down queueing.
+0x04B44mouse_button_stateBit 0 tracks left and bit 1 tracks right at window-message enqueue time.

Socket fields used by binary TCP

Offsets below are from the Socket object passed as this. They are established by the constructor and the mapped receive and send paths. The complete class layout is not yet named.

OffsetWidthWorking fieldReads and writes
+0x00684event_sinkUsed as this for event_post_socket_bytes.
+0x006C4receive_cachePoints at the object’s 0x18000 byte transport cache.
+0x480744receive_cursorIndex of the next cached transport byte.
+0x480784receive_remainingCached bytes remaining after the current byte.
+0x4807C4current_body_lengthBig-endian length assembled from the two frame header bytes.
+0x480844serial_handleSet to INVALID_HANDLE_VALUE when unused.
+0x480BC1socket_connectedCleared on recv == 0 and disconnect cleanup.
+0x480C04socketWinsock SOCKET; INVALID_SOCKET is 0xFFFFFFFF.
+0x480C50x10000wire_body_bufferReceives body bytes beginning with the server action.
+0x580C84frame_offsetCounts length-header and body bytes while a frame is open.
+0x680CCat least 0x10000decoded_or_encoded_bufferDestination for packet-body XOR transformation.
+0x780CC1transport_modeValue 5 selects the normal TCP byte path.
+0x780CD1frame_openSet after consuming 0xAA; cleared on body completion.
+0x780CF1legacy_send_sequenceDecimal digit sequence used by the printable legacy transport.
+0x780D21transfer_gateWhen set, only client action 0x10 may be queued.

Socket Event fields

The Event object created for a decoded packet has these fields:

OffsetWidthMeaningLifetime
+0x0C1Event type, value 9 for a socket packet.Set before queueing.
+0x144Owned decoded-packet pointer.Freed by the dispatcher worker after event_dispatch returns.
+0x184Decoded packet size.Positive when the pointer is present.

Structures should use unknown_XX for an established field location whose meaning remains unknown.

C Structure Layouts

This appendix presents the established 4.21 runtime layouts as C99-compatible structures. It is a programmer-oriented companion to the exact offset tables in Data Map and Runtime UI Memory Map. Those tables remain authoritative for field evidence, ownership, lifetime, and safe-reading guidance.

These declarations describe bytes in a 32-bit remote process. da_ptr32 is an address-sized value in the client, not a native pointer in a 64-bit inspector. Read a remote object into a byte buffer, validate its source pointer and vtable, and copy unaligned scalar fields with memcpy. Do not cast an arbitrary ReadProcessMemory destination to one of these packed types and then assume that every host permits unaligned access.

Unknown ranges are explicit so that each published field lands at its documented offset. A layout ending at the last known field is a prefix or view, not proof that the original C++ class ended there.

#include <stdint.h>

typedef uint32_t da_ptr32;

typedef struct da_client_point {
    int32_t top;
    int32_t left;
} da_client_point;                      /* 0x0008 bytes */

typedef struct da_client_rect {
    int32_t top;
    int32_t left;
    int32_t bottom;
    int32_t right;
} da_client_rect;                       /* 0x0010 bytes */

#pragma pack(push, 1)

Static root and vtable RVAs

These constants are RVAs, not absolute runtime addresses. Add each value to the loaded Darkages.exe module base. A root constant identifies four bytes of static pointer storage. A vtable constant identifies the relocated value expected in the first dword of a compatible live object.

#define DA_RVA_UI_BULLETIN_SESSION       0x000e2d70u
#define DA_RVA_UI_LAG_INDICATOR_PANE     0x000e2e40u
#define DA_RVA_UI_EQUIP_PANE             0x000e32a4u
#define DA_RVA_EVENT_MANAGER             0x000e32c0u
#define DA_RVA_UI_USERS_DIALOG_PANE      0x000e3564u
#define DA_RVA_UI_LOCAL_USER             0x000f5198u
#define DA_RVA_UI_MAIN_MENU_PANE         0x000f51acu
#define DA_RVA_UI_MAP_PANE               0x000f51b0u
#define DA_RVA_UI_BACKGROUND_PANE        0x000f51b4u
#define DA_RVA_NET_SOCKET                0x000f51bcu
#define DA_RVA_UI_SCREEN_PANE            0x000f51c8u
#define DA_RVA_UI_SCREEN_REGISTRY        0x000f51ccu
#define DA_RVA_EVENT_DISPATCHER          0x000f51d0u
#define DA_RVA_MEMORY_MANAGER            0x000fa160u
#define DA_RVA_UI_LOCAL_USER_ALIAS       0x000fc244u
#define DA_RVA_UI_TERMINAL_PANE          0x000fd640u

#define DA_RVA_VTABLE_BULLETIN_SESSION   0x00101e60u
#define DA_RVA_VTABLE_LAG_INDICATOR      0x00109c20u
#define DA_RVA_VTABLE_EQUIP_PANE         0x0010c6c0u
#define DA_RVA_VTABLE_EVENT_MANAGER      0x0010d720u
#define DA_RVA_VTABLE_GAME_BUTTONS       0x00110300u
#define DA_RVA_VTABLE_SKILL_INVENTORY    0x00110420u
#define DA_RVA_VTABLE_SPELL_INVENTORY    0x00110480u
#define DA_RVA_VTABLE_USERS_DIALOG       0x001106e0u
#define DA_RVA_VTABLE_SKILL_SLOT         0x00114820u
#define DA_RVA_VTABLE_SPELL_SLOT         0x00114880u
#define DA_RVA_VTABLE_LOCAL_USER         0x0011e980u
#define DA_RVA_VTABLE_NET_SOCKET         0x00126940u

For example, reading the lag value takes two remote reads. First read a da_ptr32 from module_base + DA_RVA_UI_LAG_INDICATOR_PANE, then read a da_lag_indicator_pane from the resulting heap address. Validate its first dword against module_base + DA_RVA_VTABLE_LAG_INDICATOR before using smoothed_move_rtt_ms.

Pane and hierarchy layouts

The common Pane prefix is 0xF4 bytes. The derived layouts below embed it so that a programmer can follow a Screen node to a pane, identify the vtable, and then select the matching derived view.

typedef struct da_pane_prefix {
    da_ptr32 vtable;                    /* +0x0000 */
    uint8_t unknown_0004[0x04];         /* +0x0004 */
    int32_t error_code;                 /* +0x0008 */
    uint8_t unknown_000c[0x2c];         /* +0x000C */
    da_client_rect graphic_bounds;      /* +0x0038 */
    uint8_t unknown_0048[0x60];         /* +0x0048 */
    da_client_point relative_origin;    /* +0x00A8 */
    uint8_t visible;                    /* +0x00B0 */
    uint8_t unknown_b1;                 /* +0x00B1 */
    uint8_t unknown_b2;                 /* +0x00B2 */
    uint8_t screen_payload_flag;        /* +0x00B3 */
    uint8_t unknown_00b4[0x3c];         /* +0x00B4 */
    uint8_t capture_related_state;      /* +0x00F0 */
    uint8_t unknown_00f1[0x03];         /* +0x00F1 */
} da_pane_prefix;                       /* 0x00F4 bytes */

typedef struct da_hierarchy_list {
    uint8_t unknown_0000[0x0c];         /* +0x0000 */
    uint32_t node_stride;               /* +0x000C */
    uint8_t unknown_0010[0x04];         /* +0x0010 */
    uint32_t node_count;                /* +0x0014 */
    da_ptr32 node_bytes;                /* +0x0018 */
    da_ptr32 root_or_parent_node;       /* +0x001C */
} da_hierarchy_list;                    /* 0x0020 bytes */

typedef struct da_event_hierarchy_node {
    da_ptr32 parent_node;               /* +0x00 */
    da_ptr32 child_list;                /* +0x04 */
    da_ptr32 pane;                      /* +0x08 */
    uint8_t unknown_0c[0x03];           /* +0x0C */
} da_event_hierarchy_node;              /* 0x0F-byte packed stride */

typedef struct da_screen_hierarchy_node {
    da_ptr32 parent_node;               /* +0x00 */
    da_ptr32 child_list;                /* +0x04 */
    da_ptr32 pane;                      /* +0x08 */
    da_client_rect screen_region;       /* +0x0C */
    uint8_t visible;                    /* +0x1C */
    uint8_t pane_screen_payload_flag;   /* +0x1D */
    uint8_t insertion_state;            /* +0x1E */
    uint8_t unknown_1f[0x20];           /* +0x1F */
} da_screen_hierarchy_node;             /* 0x3F-byte packed stride */

The hierarchy array itself is packed. Record two begins at array + stride, which is unaligned for both known stride values.

Event and socket layouts

typedef struct da_worker_record {
    uint32_t code;                      /* +0x00 */
    da_ptr32 data;                      /* +0x04 */
    int32_t value;                      /* +0x08 */
    da_ptr32 completion_event;          /* +0x0C */
    da_ptr32 result_buffer;             /* +0x10 */
    int32_t result_buffer_size;         /* +0x14 */
} da_worker_record;                     /* 0x18 bytes */

typedef struct da_worker_queue_base {
    da_ptr32 vtable;                    /* +0x00 */
    uint8_t unknown_04[0x08];           /* +0x04 */
    uint32_t wait_timeout_ms;           /* +0x0C */
    uint8_t wait_handle_count;          /* +0x10 */
    uint8_t unknown_11[0x03];           /* +0x11 */
    da_ptr32 wait_handles[16];          /* +0x14, index 0 is work */
    da_ptr32 work_queue;                /* +0x54 */
    da_ptr32 completion_monitor;        /* +0x58 */
    da_ptr32 completion_waiters;        /* +0x5C */
    da_ptr32 worker_thread;             /* +0x60 */
    uint32_t worker_thread_id;          /* +0x64 */
} da_worker_queue_base;                 /* 0x68 bytes */

typedef struct da_bounded_ring {
    da_ptr32 vtable;                    /* +0x00 */
    uint8_t unknown_04[0x08];           /* +0x04 */
    da_ptr32 monitor;                   /* +0x0C */
    da_ptr32 not_full_condition;        /* +0x10 */
    da_ptr32 not_empty_condition;       /* +0x14 */
    int32_t element_size;               /* +0x18 */
    int32_t capacity;                   /* +0x1C */
    da_ptr32 buffer;                    /* +0x20 */
    int32_t element_count;              /* +0x24 */
    int32_t head_index;                 /* +0x28 */
    int32_t tail_index;                 /* +0x2C */
} da_bounded_ring;                      /* 0x30 bytes */

typedef struct da_event_dispatcher_prefix {
    da_worker_queue_base worker;        /* +0x0000 */
    da_ptr32 pane_registry;             /* +0x0068 */
    uint32_t multimedia_period_ms;      /* +0x006C */
    uint32_t current_tick;              /* +0x0070 */
    uint32_t next_deadline;             /* +0x0074 */
    da_ptr32 timer_list;                /* +0x0078 */
} da_event_dispatcher_prefix;           /* known prefix through 0x007C */

typedef struct da_event_timer_record {
    da_ptr32 receiver_pane;             /* +0x00 */
    uint32_t callback_identifier;       /* +0x04 */
    uint32_t absolute_deadline;         /* +0x08 */
    uint32_t payload_first;             /* +0x0C */
    uint32_t payload_second;            /* +0x10 */
} da_event_timer_record;                /* 0x14 bytes */

typedef struct da_event_manager_input_view {
    da_worker_queue_base worker;        /* +0x0000 */
    da_ptr32 socket_object;             /* +0x0068 */
    int32_t mouse_y;                    /* +0x006C */
    int32_t mouse_x;                    /* +0x0070 */
    uint8_t unknown_0074[0x08];         /* +0x0074 */
    uint32_t double_click_time_limit;   /* +0x007C */
    uint8_t previous_click_button;      /* +0x0080 */
    uint8_t unknown_0081[0x03];         /* +0x0081 */
    int32_t previous_click_y;           /* +0x0084 */
    int32_t previous_click_x;           /* +0x0088 */
    uint32_t previous_click_time;       /* +0x008C */
    uint8_t unknown_0090[0x300];        /* +0x0090 */
    uint8_t scan_code_pressed[256];     /* +0x0390 */
    uint8_t input_modifier_state;       /* +0x0490 */
    uint8_t unknown_0491[0x1f];         /* +0x0491 */
    uint32_t input_block_flags;         /* +0x04B0 */
    uint32_t mouse_button_state;        /* +0x04B4 */
} da_event_manager_input_view;          /* known prefix through 0x04B8 */

typedef struct da_socket_421 {
    da_worker_queue_base worker;        /* +0x00000 */
    da_ptr32 event_sink;                /* +0x00068 */
    da_ptr32 receive_cache;             /* +0x0006C */
    uint8_t unknown_00070[0x48004];     /* +0x00070 */
    uint32_t receive_cursor;            /* +0x48074 */
    uint32_t receive_remaining;         /* +0x48078 */
    uint32_t current_body_length;       /* +0x4807C */
    uint8_t unknown_48080[0x04];        /* +0x48080 */
    da_ptr32 serial_handle;             /* +0x48084 */
    uint8_t unknown_48088[0x34];        /* +0x48088 */
    uint8_t socket_connected;           /* +0x480BC */
    uint8_t unknown_480bd[0x03];        /* +0x480BD */
    uint32_t socket;                    /* +0x480C0 */
    uint8_t unknown_480c4;              /* +0x480C4 */
    uint8_t wire_body_buffer[0x10000];  /* +0x480C5 */
    uint8_t unknown_580c5[0x03];        /* +0x580C5 */
    uint32_t frame_offset;              /* +0x580C8 */
    uint8_t unknown_580cc[0x10000];     /* +0x580CC */
    uint8_t decoded_or_encoded_buffer[0x10000]; /* +0x680CC */
    uint8_t transport_mode;             /* +0x780CC */
    uint8_t frame_open;                 /* +0x780CD */
    uint8_t unknown_780ce;              /* +0x780CE */
    uint8_t legacy_send_sequence;       /* +0x780CF */
    uint8_t unknown_780d0[0x02];        /* +0x780D0 */
    uint8_t transfer_gate;              /* +0x780D2 */
    uint8_t unknown_780d3;              /* +0x780D3 */
} da_socket_421;                        /* 0x780D4 bytes */

typedef struct da_socket_event {
    da_ptr32 vtable;                    /* +0x00 */
    uint8_t unknown_04[0x08];           /* +0x04 */
    uint8_t event_type;                 /* +0x0C, value 9 */
    uint8_t unknown_0d[0x07];           /* +0x0D */
    da_ptr32 decoded_packet;            /* +0x14 */
    uint32_t decoded_packet_size;       /* +0x18 */
    uint8_t unknown_1c[0x08];           /* +0x1C */
} da_socket_event;                      /* 0x24 bytes */

da_worker_queue_base.wait_handles[0] is the native work semaphore. The worker tolerates that semaphore being signaled while work_queue is empty and still invokes its periodic vtable slot. This is the established wake primitive for the proposed worker-affine Event Proxy pumps. Do not modify da_bounded_ring indices or counts from an inspector. Socket and EventMan rings have capacity 0x80; the dispatcher ring has capacity 0x400.

Persistent game UI layouts

typedef struct da_lag_indicator_pane {
    da_pane_prefix pane;                /* +0x0000 */
    uint32_t smoothed_move_rtt_ms;      /* +0x00F4 */
} da_lag_indicator_pane;                /* 0x00F8 bytes */

typedef struct da_game_buttons_pane {
    da_pane_prefix pane;                /* +0x0000 */
    da_ptr32 map_pane;                  /* +0x00F4 */
    uint8_t unknown_00f8[0x08];         /* +0x00F8 */
    uint8_t equipment_selected;         /* +0x0100 */
    uint8_t unknown_0101[0x07];         /* +0x0101 */
    uint8_t skill_selected;             /* +0x0108 */
    uint8_t unknown_0109[0x07];         /* +0x0109 */
    uint8_t spell_selected;             /* +0x0110 */
    uint8_t unknown_0111[0x07];         /* +0x0111 */
    uint8_t chat_selected;              /* +0x0118 */
    uint8_t unknown_0119[0x07];         /* +0x0119 */
    uint8_t status_selected;            /* +0x0120 */
    uint8_t unknown_0121[0x03];         /* +0x0121 */
    da_ptr32 current_content_pane;      /* +0x0124 */
    uint8_t unknown_0128[0x08];         /* +0x0128 */
    da_ptr32 chat_pane;                 /* +0x0130 */
    da_ptr32 status_pane;               /* +0x0134 */
    da_ptr32 equip_pane;                /* +0x0138 */
    da_ptr32 skill_inventory_pane;      /* +0x013C */
    da_ptr32 spell_inventory_pane;      /* +0x0140 */
    da_ptr32 system_message_pane;       /* +0x0144 */
    uint8_t unknown_0148[0x10];         /* +0x0148 */
} da_game_buttons_pane;                 /* 0x0158 bytes */

typedef struct da_inventory_parent_pane {
    da_pane_prefix pane;                /* +0x0000 */
    da_ptr32 slot_panes[36];            /* +0x00F4, entry 0 is slot 1 */
    uint8_t state_184;                  /* +0x0184 */
    uint8_t unknown_0185[0x03];         /* +0x0185 */
} da_inventory_parent_pane;             /* 0x0188 bytes */

typedef struct da_skill_slot_pane {
    da_pane_prefix pane;                /* +0x0000 */
    uint16_t display_id;                /* +0x00F4 */
    char name[0x80];                    /* +0x00F6 */
    uint8_t unknown_0176[0x100];        /* +0x0176 */
    uint8_t slot;                       /* +0x0276, 1 through 36 */
    uint8_t pointer_capture_state;      /* +0x0277 */
    uint8_t cooldown_active;            /* +0x0278 */
    uint8_t unknown_0279[0x03];         /* +0x0279 */
    da_client_point pointer_down_point; /* +0x027C */
    da_client_rect tracking_rect;       /* +0x0284 */
} da_skill_slot_pane;                   /* 0x0294 bytes */

typedef struct da_spell_slot_pane {
    da_pane_prefix pane;                /* +0x0000 */
    uint8_t slot;                       /* +0x00F4, 1 through 36 */
    uint8_t unknown_00f5;               /* +0x00F5 */
    uint16_t display_id;                /* +0x00F6 */
    int8_t spell_type;                  /* +0x00F8 */
    char name[0x80];                    /* +0x00F9 */
    char comment[0x80];                 /* +0x0179 */
    uint8_t packet_flag;                /* +0x01F9 */
    uint8_t pointer_capture_state;      /* +0x01FA */
    uint8_t cooldown_active;            /* +0x01FB */
    da_client_point pointer_down_point; /* +0x01FC */
    da_client_rect tracking_rect;       /* +0x0204 */
} da_spell_slot_pane;                   /* 0x0214 bytes */

The skill and spell parent layouts are byte-identical in the published range. Select the child structure only after validating the parent and child vtable RVAs.

Character, bulletin, and users layouts

typedef struct da_equip_pane {
    da_pane_prefix pane;                /* +0x0000 */
    uint8_t unknown_00f4[0x45c];        /* +0x00F4 */
    uint8_t appearance[4];              /* +0x0550 */
    char self_look_554[0x100];          /* +0x0554 */
    char self_look_654[0x80];           /* +0x0654 */
    char self_look_6d4[0x80];           /* +0x06D4 */
    char self_look_754[0x80];           /* +0x0754 */
    char self_look_7d4[0x100];          /* +0x07D4, established minimum */
    uint8_t unknown_08d4[0x24e];        /* +0x08D4 */
    uint8_t unknown_0b22[0x02];         /* +0x0B22 */
    uint16_t item_id_by_slot[13];       /* +0x0B24, entry 0 is slot 1 */
    char item_name_by_slot[13][0x80];   /* +0x0B3E */
    uint8_t unknown_11be[0x02];         /* +0x11BE */
    uint32_t unknown_11c0;              /* +0x11C0 */
    uint8_t unknown_11c4;               /* +0x11C4 */
    uint8_t unknown_11c5[0x03];         /* +0x11C5 */
    da_ptr32 helper_pane;               /* +0x11C8 */
    da_ptr32 legend_dialog_pane;        /* +0x11CC */
    uint8_t unknown_11d0;               /* +0x11D0 */
    uint8_t unknown_11d1[0x03];         /* +0x11D1 */
} da_equip_pane;                        /* 0x11D4 bytes */

typedef struct da_bulletin_session {
    da_pane_prefix pane;                /* +0x0000 */
    uint8_t unknown_00f4;               /* +0x00F4 */
    uint8_t current_history_index;      /* +0x00F5 */
    uint8_t unknown_00f6[0x02];         /* +0x00F6 */
    da_ptr32 child_history[10];         /* +0x00F8 */
    da_ptr32 active_child_pane;         /* +0x0120 */
    uint8_t request_wait_state;         /* +0x0124 */
    uint8_t unknown_0125[0x07];         /* +0x0125 */
} da_bulletin_session;                  /* 0x012C bytes */

typedef struct da_users_dialog_pane {
    da_pane_prefix pane;                /* +0x0000 */
    uint8_t unknown_00f4[0x45c];        /* +0x00F4 */
    da_ptr32 row_or_list_objects[9];    /* +0x0550 */
    da_ptr32 list_storage_object;       /* +0x0574 */
    uint8_t unknown_0578;               /* +0x0578 */
    uint8_t unknown_0579;               /* +0x0579 */
    uint8_t unknown_057a[0x02];         /* +0x057A */
    da_ptr32 scrolling_control;         /* +0x057C */
    uint8_t unknown_0580;               /* +0x0580 */
    uint8_t unknown_0581;               /* +0x0581 */
    uint8_t unknown_0582[0x06];         /* +0x0582 */
} da_users_dialog_pane;                 /* 0x0588 bytes */

self_look_7d4 is represented as the established minimum 0x100-byte region. The following unknown range keeps later fields at their confirmed offsets without asserting that the original source declared exactly that array size.

Local user movement timing

ui_local_user at Darkages.exe:0x004F5198 points to a 0x776C-byte object while the game UI is active. Only the timing slice needed for movement and the lag indicator is named here.

typedef struct da_local_user_timing_view {
    uint8_t unknown_0000[0x6f28];       /* +0x0000 */
    uint8_t move_send_sequence;         /* +0x6F28 */
    uint8_t move_sequence_at_last_smove;/* +0x6F29 */
    uint8_t unknown_6f2a[0x02];         /* +0x6F2A */
    uint32_t last_move_send_tick;       /* +0x6F2C */
    uint8_t unknown_6f30[0x04];         /* +0x6F30 */
    uint32_t last_move_round_trip_ms;   /* +0x6F34 */
    uint8_t unknown_6f38[0x834];        /* +0x6F38 */
} da_local_user_timing_view;            /* 0x776C bytes */

#pragma pack(pop)

last_move_send_tick is overwritten on every CMove, so last_move_round_trip_ms is not a per-sequence latency table. It is the unsigned timeGetTime difference between an SMove and the most recently queued CMove. The lag pane stores a separate smoothed value at its own +0xF4.

Validation checklist

Before interpreting any remote bytes:

  1. Resolve the loaded module base and add the documented RVA.
  2. Read the static pointer and reject null outside the expected lifecycle phase.
  3. Confirm that the complete requested range is readable.
  4. Validate the first dword against the expected relocated vtable where one is known.
  5. Copy bounded strings and require a NUL terminator inside the published buffer.
  6. Re-read the static pointer and vtable after the copy. Retry if either changed.
  7. Treat packed hierarchy nodes and odd socket offsets as unaligned data.

Raw writes are not structure-aware UI operations. Use these layouts for observation first. A controlled writer should invoke the established client method on the client’s UI or event path so that registries, invalidation, focus, timers, and ownership remain consistent.

Runtime UI Memory Map

The 4.21 client exposes enough stable process state to build a useful read-only UI inspector. Static module globals lead to the Screen composition tree, the Event receiver tree, and several important persistent panes. Heap pane objects share a common base layout, and their first pointer identifies the concrete implementation through a module-relative virtual-table address.

This page is a runtime lookup reference. It describes addresses and fields confirmed by code paths that construct, read, update, or destroy them. The broader static global catalog is in Data Map, packed programmer-facing declarations are in C Structure Layouts, and the complete pane-class discriminator list is in Pane Virtual Table Inventory. Event Proxy IPC, Rules, and State applies these paths to late-attach snapshots and guarded peek or poke requests.

High-level operation

Address model

Darkages.exe is a 32-bit image with preferred image base 0x00400000. Every pointer and uintptr_t value discussed here is four bytes. The documented virtual addresses identify the analyzed image, but an external reader should resolve the loaded module base and add an RVA:

uintptr_t runtime_address = loaded_module_base + documented_rva;

Do not assume that Darkages.exe loaded at its preferred base. A virtual-table address is also relocated with the module. For example, a live GameButtons pane is identified by:

int is_game_buttons_pane =
    pane_vtable == loaded_module_base + 0x00110300;

Static runtime roots

Each address in the table is storage in the image. Read one 32-bit pointer from loaded_module_base + RVA to obtain the heap object.

Static VARVACurrent IDA nameHeap object reachedUseful next step
Darkages.exe:0x004E2D700x000E2D70ui_bulletin_sessionActive BulletinSessionRead active child at session +0x120.
Darkages.exe:0x004E2E400x000E2E40ui_lag_indicator_panePersistent lag indicator paneRead its smoothed movement round-trip value at +0xF4.
Darkages.exe:0x004E32A40x000E32A4ui_equip_panePersistent User Equip paneRead visibility, equipment slots, self-look strings, and legend child pointers.
Darkages.exe:0x004E35640x000E3564ui_users_dialog_paneUsers Dialog PaneRead visibility and its nine row/list objects.
Darkages.exe:0x004F51980x000F5198ui_local_userActive local user objectRead movement timing at +0x6F2C/+0x6F34.
Darkages.exe:0x004F51AC0x000F51ACui_main_menu_paneMainMenuPaneDistinguishes the main-menu phase.
Darkages.exe:0x004F51B00x000F51B0ui_map_paneMapPaneDistinguishes the in-game phase and leads to map-owned state.
Darkages.exe:0x004F51B40x000F51B4ui_background_paneBackgroundPanePersistent parent of the in-game UI.
Darkages.exe:0x004F51C80x000F51C8ui_screen_paneRoot ScreenPanePersistent after successful initialization.
Darkages.exe:0x004F51CC0x000F51CCui_screen_registryScreen hierarchy listRead root node pointer at list +0x1C.
Darkages.exe:0x004F51D00x000F51D0event_dispatcherEvent dispatcherRead Event pane hierarchy at dispatcher +0x68.
Darkages.exe:0x004FD6400x000FD640ui_terminal_paneTerminalPaneDistinguishes terminal/bootstrap phase.

These roots are nullable according to lifecycle. A null pointer is ordinarily a phase or ownership result, not an error in the reader.

What the two pane trees reveal

The Screen tree is the primary inventory of composed UI. It contains persistent panes, hidden content panes that remain attached, dialogs, controls, and temporary slot panes. Its parent-child links also describe UI ownership and placement.

The Event tree is an independent inventory of panes registered for mouse, keyboard, socket-packet, or timer delivery. It is useful as a cross-check and can contain an object whose Screen membership is changing. Neither tree is an ownership-safe API for an external process. Both can change while they are read.

A practical read-only inspector can:

  1. Snapshot the Screen tree.
  2. Validate each pane pointer and vtable.
  3. Read the common visibility byte and bounds.
  4. Label known classes by vtable RVA.
  5. Follow class-specific pointers only after confirming the expected vtable.
  6. Optionally snapshot the Event tree and mark which visible or hidden panes currently receive Events.

Useful observation recipes

List current panes

Read ui_screen_registry, then its root at registry + 0x1C. Observe the root pane at root + 0x08, and recursively snapshot the child list at root + 0x04. For every node, read the pane pointer at node +0x08. Read the live pane’s vtable, local bound rectangle at +0x38, relative top/left origin at +0xA8, and visible byte at +0xB0.

The full vtable inventory contains construction and control variants as well as visible screens. A vtable match identifies the object’s current implementation, but does not imply that the pane is visible or top-level.

Find the selected game content

There is no separate static root for GameButtonsPane. Find the pane whose vtable is loaded_module_base + 0x00110300. Its field at +0x124 points to the currently selected content pane. Compare that pointer with fields +0x130 through +0x140 to label the selection as chat, status, equipment, skills, or spells.

This pointer records selection, while the selected pane’s +0xB0 byte records current visibility. Equipment can be the selected object while hidden because selecting equipment twice toggles its visibility.

Read skills and spells

From GameButtonsPane, read the skill inventory at +0x13C and the spell inventory at +0x140. Each parent owns a 36-entry pointer array beginning at +0xF4. Array entry zero represents protocol slot 1:

uint32_t slot_pane =
    read_process_u32(inventory + 0x00f4 + 4 * (slot - 1));

A null entry is an empty slot. A non-null skill entry should use vtable RVA 0x00114820; a spell entry should use 0x00114880. The child objects retain the server-provided display ID and strings, their 1-based slot, pointer interaction state, and cooldown-active state.

Read equipment and self-look state

Read ui_equip_pane directly. The normal equipment range is 1 through 13. For a 1-based slot:

uintptr_t item_id_address = equip_pane + 0x0b22 + 2 * slot;
uintptr_t item_name_address =
    equip_pane + 0x0b3e + 0x80 * (slot - 1);

The identifier is a native little-endian uint16_t in memory, even though the server packet supplied it in big-endian order. The name is a NUL-terminated byte string in a 0x80-byte record. The pane also retains the most recent SSelfLook appearance bytes, strings, a 32-bit value, and two child-pane pointers. Several field meanings remain unknown, so the code-level table uses unknown_XX names.

Read bulletin, mail, and users state

ui_bulletin_session points to a specialized Pane with vtable RVA 0x00101E60. Its current child pointer at +0x120 leads to the active bulletin or mail dialog. Session +0xF5 records its current child-history position and the one-byte field at +0x124 records request-wait state.

ui_users_dialog_pane is a Pane with a direct static root. It owns nine heap row/list objects at +0x550 through +0x570. The dialog remains allocated and is reused by later SShowUsers replies, so +0xB0 is the useful shown/hidden test.

Read movement timing and the lag indicator

Read ui_local_user while the game UI is active. Its latest queued CMove timestamp is a 32-bit timeGetTime value at +0x6F2C, and the latest SMove response sample is at +0x6F34. The persistent lag pane has its own direct root, ui_lag_indicator_pane, and stores the displayed smoothed value at pane +0xF4.

The raw sample and displayed sample are intentionally different. On every accepted SMove, the pane replaces its value with (old_value + latest_sample) / 2. A reader that wants to reproduce the visible band should use the pane field. A reader that wants the latest unsmoothed movement response should use the local-user field.

Peek versus poke

Read-only observation is practical with the layouts on this page. Raw writes are not equivalent to client UI operations.

Changing Pane + 0xB0 does not perform invalidation, capture release, focus changes, or parent redraw. Replacing a child pointer does not update either packed hierarchy and can create a use-after-free during Event traversal. Editing a vtable or packed node can redirect a virtual call. Editing a fixed string without preserving its limit and NUL termination can overwrite adjacent state.

If controlled UI mutation is investigated later, the safer design is to invoke the established pane method on the client UI path and let it update both registries and rendering state. This page intentionally documents a read-only snapshot model.

Code-level memory map

Packed hierarchy list

Both Screen and Event registries use the same packed hierarchy list. Multi-byte fields in a node may be unaligned, so an external reader should copy bytes or use an unaligned-safe read helper instead of casting a local aligned structure over remote memory.

List offsetWidthMeaning
+0x0C4Node stride.
+0x144Node count.
+0x184Pointer to contiguous packed node bytes.
+0x1C4Root or parent node pointer. Screen stores its separately allocated root here.
Common node offsetWidthMeaning
+0x004Parent node pointer.
+0x044Child hierarchy-list pointer, or null.
+0x084Pane pointer, the first field of both known payloads.

Screen requests a 0x34-byte payload, so its node stride is 0x3F. Event requests a four-byte payload, so its stride is 0x0F.

Screen node offsetWidthMeaning
+0x084Pane pointer.
+0x0C16Screen-owned rectangle or region state, initialized to zero on insertion. This is not the Pane bounds field.
+0x1C1Visible state copied from ui_pane_is_visible during insertion and maintained by Screen operations.
+0x1D1Pane +0xB3 flag copied during insertion.
+0x1E1Initialized to zero during insertion.
+0x1Fremaining payloadAdditional Screen-owned state whose fields are not yet published.

The runtime entry chains are:

uintptr_t screen_list =
    read_process_u32(module_base + 0x000f51cc);
uintptr_t screen_root = read_process_u32(screen_list + 0x1c);
uintptr_t screen_children = read_process_u32(screen_root + 0x04);

uintptr_t dispatcher =
    read_process_u32(module_base + 0x000f51d0);
uintptr_t event_list = read_process_u32(dispatcher + 0x68);

Common Pane fields

Pane offsetWidthMeaningReader use
+0x00004Current vtable pointer.Convert to an RVA relative to the loaded module and match the inventory.
+0x00084Last propagated Screen or dispatcher error.Diagnostic state, not a class identifier.
+0x003816Local graphic bound rectangle.Four signed 32-bit values in client order: top, left, bottom, right.
+0x00A88Relative origin point.Signed 32-bit top at +0xA8 and left at +0xAC.
+0x00B01Visible or active byte.Nonzero means shown according to ui_pane_is_visible.
+0x00B11unknown_B1 constructor flag.Established location, semantics not established.
+0x00B21unknown_B2 constructor flag.Established location, semantics not established.
+0x00B31Screen payload flag.Copied into Screen node +0x1D.
+0x00F01Capture-related state.Checked before Screen removal.

ui_pane_set_bound_rect retains the input rectangle’s top and left at +0xA8/+0xAC, translates the rectangle to a local origin, and applies it to the graphic bound rectangle at +0x38. ui_pane_get_bound_rect reverses that translation. The in-parent rectangle can therefore be reconstructed without a client call:

int32_t relative_top = pane_bound_top + pane_origin_top;
int32_t relative_left = pane_bound_left + pane_origin_left;
int32_t relative_bottom = pane_bound_bottom + pane_origin_top;
int32_t relative_right = pane_bound_right + pane_origin_left;

These client rectangles use top, left, bottom, right field order, not the Win32 RECT memory order. ui_screen_get_pane_origin then finds the Screen node and accumulates parent placement. An external tree walker can preserve the parent chain and perform the same accumulation when absolute screen coordinates are needed.

Runtime class discriminators

The following vtables are especially useful for state walking. The address to compare at runtime is loaded_module_base + RVA.

Class or roleStatic VARVACurrent IDA name
BulletinSessionDarkages.exe:0x00501E600x00101E60ui_bulletin_session_vtable
Lag indicator paneDarkages.exe:0x00509C200x00109C20ui_lag_indicator_pane_vtable
GameButtonsPaneDarkages.exe:0x005103000x00110300ui_game_buttons_pane_vtable
User Equip paneDarkages.exe:0x0050C6C00x0010C6C0ui_equip_pane_vtable
Users Dialog PaneDarkages.exe:0x005106E00x001106E0ui_users_dialog_pane_vtable
Skill inventory parentDarkages.exe:0x005104200x00110420ui_skill_inventory_pane_vtable
Spell inventory parentDarkages.exe:0x005104800x00110480ui_spell_inventory_pane_vtable
Skill slot childDarkages.exe:0x005148200x00114820ui_skill_slot_pane_vtable
Spell slot childDarkages.exe:0x005148800x00114880ui_spell_slot_pane_vtable
NewUser local-user paneDarkages.exe:0x0051E9800x0011E980ui_local_user_vtable

Lag indicator and movement timing

The lag indicator is a 0xF8-byte Pane created with the rest of the in-game pane graph. Its constructor publishes ui_lag_indicator_pane, initializes +0xF4 to 300 milliseconds, sets a 32 by 32 local bound, and adds and registers the pane below BackgroundPane. The attach method passes left = 537, top = 314, right = 549, and bottom = 328 to ui_rect_init. The helper stores the client-order words (top = 314, left = 537, bottom = 328, right = 549), placing the visible indicator at x = 537..549, y = 314..328.

The value is not based on socket throughput, receive queue depth, or a periodic ping packet. net_c_send_move builds CMove action 0x06, increments the one-byte movement sequence at local user +0x6F28, and stores timeGetTime at +0x6F2C. When the local user receives SMove action 0x0B, ui_local_user_handle_smove stores the unsigned difference between the new clock sample and +0x6F2C at +0x6F34. It then calls ui_lag_indicator_update_from_local_user, which averages the new sample with pane +0xF4 and invalidates the pane rectangle.

uint32_t latest_sample = current_tick - last_move_send_tick;
uint32_t displayed_sample =
    (previous_displayed_sample + latest_sample) / 2;

The sender keeps only one timestamp. If another CMove is queued before an earlier SMove arrives, the newer send overwrites +0x6F2C. The packet byte read at decoded-packet offset +0x0A is also discarded rather than compared against the client movement sequence. The indicator is therefore a smoothed response time relative to the most recent move timestamp, not a rigorous per-sequence network RTT measurement.

ui_lag_indicator_pane_draw selects statcon.epf frames with unsigned comparisons:

Smoothed value at pane +0xF4FrameVisual color in the checked version-family assetNotes
02GreenZero bypasses the 1 through 249 fast band. Normal construction starts at 300, not zero.
1 through 2493BlueFastest band.
250 through 3492GreenConstructor default 300 starts here.
350 through 4491OrangeIntermediate warning band.
450 or more0RedSlowest band.

The frame numbers and thresholds come directly from the 4.21 draw code. The color labels were checked by decoding the same-named statcon.epf against legend.pal from an available 4.51 data set: frame 3 is blue, frame 2 green, frame 1 orange, and frame 0 red. Exact 4.21 asset identity was not available in this workspace, so tools that require byte-identical 4.21 colors should verify those four asset frames while retaining the code-established frame mapping.

Object offsetWidthMeaning
lag pane +0x00F44Unsigned smoothed movement response value used for frame selection.
local user +0x6F281Incrementing CMove sequence byte.
local user +0x6F291Current movement sequence copied when an SMove is handled. It is not populated from the packet byte read at +0x0A.
local user +0x6F2C4timeGetTime value from the most recently queued CMove.
local user +0x6F344Latest unsmoothed SMove minus latest-CMove clock difference.

See Pane Virtual Table Inventory for dialogs, bulletin and mail panes, controls, MapPane, options, books, effects, and the remaining compatible tables.

GameButtonsPane fields

GameButtonsPane is constructed by ui_game_buttons_pane_ctor and found dynamically by its vtable. Its direct links make it the most useful bridge from generic pane enumeration to persistent in-game UI state.

OffsetWidthMeaning
+0x00F44MapPane pointer passed to the constructor. It normally matches ui_map_pane.
+0x01001Equipment button selected state.
+0x01081Skill button selected state.
+0x01101Spell button selected state.
+0x01181Chat button selected state.
+0x01201Status button selected state.
+0x01244Current content-pane pointer.
+0x01304Chat pane pointer.
+0x01344Status pane pointer.
+0x01384Equipment pane pointer. It normally matches ui_equip_pane.
+0x013C4Skill inventory parent pointer.
+0x01404Spell inventory parent pointer.
+0x01444System-message pointer passed to the constructor.

The five selection methods hide the previous +0x124 pane, show the chosen field, and replace +0x124. The button-state bytes are also updated so exactly one ordinary content button is selected.

Skill inventory and slot panes

The skill inventory parent has vtable ui_skill_inventory_pane_vtable. It owns exactly 36 heap pointers.

Parent offsetWidthMeaning
+0x00F436 * 4Slot pane pointers for protocol slots 1 through 36.

SAddSkill replaces the selected entry with a 0x294-byte pane. SRemoveSkill unregisters, removes, deletes, and clears the entry. SCooldown marks the selected child and later clears it through a parent timer.

Skill slot offsetWidthMeaning
+0x00004ui_skill_slot_pane_vtable.
+0x00F42Server-provided big-endian display ID, stored in native little-endian form.
+0x00F6variable, maximum observed name length 127NUL-terminated skill name.
+0x027611-based skill slot. This byte is sent by CUseSkill.
+0x02771Pointer-capture or pressed state used by the mouse handler.
+0x02781Cooldown-active flag. ui_skill_slot_start_cooldown sets it.
+0x027C8Stored pointer-down point, two signed 32-bit coordinates.
+0x028416Pointer-tracking client rectangle.

Spell inventory and slot panes

The spell inventory parent mirrors the skill parent and also owns 36 heap pointers at +0xF4.

Spell slot offsetWidthMeaning
+0x00004ui_spell_slot_pane_vtable.
+0x00F411-based spell slot. This byte is sent by the immediate CUseSpell path.
+0x00F62Server-provided big-endian display ID, stored in native little-endian form.
+0x00F81Signed spell type, established range 1 through 8. It selects the client activation path.
+0x00F90x80NUL-terminated spell name buffer.
+0x01790x80NUL-terminated spell comment buffer.
+0x01F91unknown_1F9, copied from the trailing SAddSpell byte and passed to SpellBookDialog.
+0x01FA1Pointer-capture or pressed state used by the mouse handler.
+0x01FB1Cooldown-active flag. ui_spell_slot_start_cooldown sets it.
+0x01FC8Stored pointer-down point, two signed 32-bit coordinates.
+0x020416Pointer-tracking client rectangle.

Equipment and self-look fields

The User Equip pane uses vtable ui_equip_pane_vtable and has a direct static root. The item fields use a 1-based protocol slot directly in the identifier expression, while names use a zero-based record index.

Offset or expressionWidthMeaning
+0x0550 through +0x05534Four appearance bytes copied from the packet.
+0x05540x100NUL-terminated unknown_554 self-look string buffer.
+0x06540x80NUL-terminated unknown_654 self-look string buffer.
+0x06D40x80NUL-terminated unknown_6D4 self-look string buffer.
+0x07540x80NUL-terminated unknown_754 self-look string buffer.
+0x07D4at least 0x100NUL-terminated unknown_7D4 self-look string buffer.
+0x0B22 + 2 * slot2Item identifier for 1-based equipment slot 1 through 13.
+0x0B3E + 0x80 * (slot - 1)0x80NUL-terminated item name record for slot 1 through 13.
+0x11C04unknown_11C0 big-endian packet value stored in host order.
+0x11C41unknown_11C4 packet byte.
+0x11C84Owned helper-pane pointer, allocated by the equipment constructor.
+0x11CC4Owned LegendDialogPane pointer.
+0x11D01unknown_11D0 state flag initialized to zero.

The five strings are updated as one SSelfLook transaction. A snapshot reader should copy each bounded buffer and accept it only if the pane pointer and vtable remain unchanged after the copy.

BulletinSession fields

Session offsetWidthMeaning
+0x00004ui_bulletin_session_vtable.
+0x00F51Current child-history index.
+0x00F810 * 4Ten child-dialog history pointers.
+0x01204Active bulletin or mail child pane.
+0x01241Request-wait state used to suppress overlapping operations.

The session is a Pane and can be validated against vtable RVA 0x00101E60. Its active child at +0x120 is another Pane with its own bulletin or mail class vtable.

Users Dialog Pane fields

Pane offsetWidthMeaning
+0x00004ui_users_dialog_pane_vtable.
+0x05509 * 4Nine owned heap row/list object pointers.
+0x05744Additional owned list-storage object.
+0x05781unknown_578 state flag.
+0x05791unknown_579 state flag.
+0x057C4Owned list or scrolling control pointer.
+0x05811unknown_581 state flag.

Row record internals are not yet mapped to the documentation threshold. The direct pane root, visibility, common bounds, and nine owned objects are established.

Stable external snapshot procedure

The client does not expose a lock for external readers. Screen insertion and removal can relocate a list’s packed node array, and deferred deletion can free a pane after it disappears from a registry. Use retryable snapshots:

  1. Read list stride, count, array pointer, and root pointer.
  2. Reject an unexpected stride, an implausible count, integer overflow in count * stride, a null array with nonzero count, or an unreadable byte range.
  3. Copy count * stride bytes with one ReadProcessMemory operation.
  4. Re-read the four metadata fields and discard the copy if any changed.
  5. Parse nodes from the local byte copy with unaligned-safe reads.
  6. Snapshot each non-null child list recursively.
  7. Before reading a pane, confirm that its first four bytes are readable and that its vtable is a known module address.
  8. After copying class-specific state, re-read the pane’s vtable and the owner pointer that led to it. Discard the object snapshot if either changed.

The core packed-node calculation is:

static uintptr_t packed_node_address(
    uintptr_t node_array,
    uint32_t node_stride,
    uint32_t node_index)
{
    return node_array + (uintptr_t)node_stride * node_index;
}

Do not cast the remote 0x3F- or 0x0F-byte nodes to an ordinary local C structure. Their second and later records are not four-byte aligned.

Function table

AddressCurrent IDA namePrototypePurposeCall relationships and notes
Darkages.exe:0x0040E660ui_bulletin_session_deleting_dtorestablished __thiscall deleting destructorDestroy the active BulletinSession and its child history.Clears ui_bulletin_session and unregisters and removes the Pane.
Darkages.exe:0x0040EB10ui_bulletin_session_ctorvoid *__thiscall(void *, int, const uint8_t *)Construct and publish a 0x12C-byte BulletinSession Pane.Initializes the ten-entry history and one-byte request-wait field.
Darkages.exe:0x00423984ui_lag_indicator_pane_dtorvoid __thiscall(struct ui_lag_indicator_pane *)Destroy the lag indicator Pane.Clears ui_lag_indicator_pane before base Pane cleanup.
Darkages.exe:0x004239C0ui_get_lag_indicator_panestruct ui_lag_indicator_pane *__cdecl(void)Return the persistent lag indicator pointer.Used by the SMove timing path and game-pane teardown.
Darkages.exe:0x004239D0ui_lag_indicator_pane_ctorstruct ui_lag_indicator_pane *__thiscall(struct ui_lag_indicator_pane *, void *)Construct and publish the 0xF8-byte lag indicator Pane.Initializes the displayed value to 300 and calls the attach method.
Darkages.exe:0x00423A44ui_lag_indicator_pane_attachvoid __thiscall(struct ui_lag_indicator_pane *, void *)Add and register the lag indicator below BackgroundPane.Uses left/top/right/bottom arguments (537, 314, 549, 328).
Darkages.exe:0x00423AA0ui_lag_indicator_pane_drawvoid __thiscall(struct ui_lag_indicator_pane *)Select and draw a statcon.epf frame.Vtable slot +0x48; applies the five value bands documented above.
Darkages.exe:0x00423B60ui_lag_indicator_update_from_local_uservoid __thiscall(struct ui_lag_indicator_pane *)Average the latest local-user timing sample into the displayed value.Reads local user +0x6F34, writes pane +0xF4, and invalidates the Pane.
Darkages.exe:0x00431150event_dispatcher_register_panevoid __thiscall(void *, void *, int, int)Insert a pane into the Event hierarchy.Event list is reached at dispatcher +0x68.
Darkages.exe:0x0043A5F0ui_game_buttons_pane_ctorvoid *__thiscall(void *, void *, void *)Construct GameButtonsPane and its persistent content graph.Installs ui_game_buttons_pane_vtable.
Darkages.exe:0x0043CF20ui_game_buttons_handle_key_eventint __thiscall(void *, void *)Select equipment, skills, spells, chat, or status.Writes button state and calls ui_game_buttons_select_content.
Darkages.exe:0x00442A34ui_skill_inventory_apply_add_packetint __thiscall(void *, const uint8_t *)Parse SAddSkill and replace a slot child.Calls remove and create methods.
Darkages.exe:0x00442C04ui_skill_inventory_remove_slot_panevoid __thiscall(void *, uint8_t)Remove and delete one skill slot pane.Clears one pointer at parent +0xF4.
Darkages.exe:0x00442D14ui_skill_inventory_create_slot_panevoid __thiscall(void *, uint8_t, uint16_t, const char *)Allocate and register one skill slot pane.Object size 0x294.
Darkages.exe:0x00443BB4ui_spell_inventory_apply_add_packetint __thiscall(void *, const uint8_t *)Parse SAddSpell and replace a slot child.Calls remove and create methods.
Darkages.exe:0x00443E14ui_spell_inventory_remove_slot_panevoid __thiscall(void *, uint8_t)Remove and delete one spell slot pane.Clears one pointer at parent +0xF4.
Darkages.exe:0x00443F24ui_spell_inventory_create_slot_panevoid __thiscall(void *, uint8_t, uint16_t, int8_t, const char *, const char *, uint8_t)Allocate and register one spell slot pane.Object size 0x214.
Darkages.exe:0x00446090ui_point_initvoid __cdecl(void *, int, int)Initialize an eight-byte client point.Used for Pane relative-origin fields.
Darkages.exe:0x004460B0ui_rect_initvoid __cdecl(void *, int, int, int, int)Initialize a client rectangle from left, top, right, and bottom.Stores fields in top, left, bottom, right memory order.
Darkages.exe:0x0044E4D0ui_hierarchy_list_ctorvoid *__thiscall(void *, int)Construct a packed hierarchy list.Node stride is payload size plus 0x0B.
Darkages.exe:0x00454CA0ui_skill_slot_finish_cooldownestablished __thiscall methodClear a skill slot’s cooldown-active byte.Called by the parent inventory timer path.
Darkages.exe:0x00454CC0ui_skill_slot_start_cooldownestablished __thiscall methodSet a skill slot’s cooldown-active byte.Reached from SCooldown processing.
Darkages.exe:0x00456220ui_spell_slot_finish_cooldownestablished __thiscall methodClear a spell slot’s cooldown-active byte.Called by the parent inventory timer path.
Darkages.exe:0x00456240ui_spell_slot_start_cooldownestablished __thiscall methodSet a spell slot’s cooldown-active byte.Reached from SCooldown processing.
Darkages.exe:0x00456B90ui_skill_slot_pane_ctorvoid *__thiscall(void *, uint8_t, uint16_t, const char *)Initialize a skill slot’s persistent fields.Installs ui_skill_slot_pane_vtable.
Darkages.exe:0x00456C70ui_spell_slot_pane_ctorvoid *__thiscall(void *, uint8_t, uint16_t, int8_t, const char *, const char *, uint8_t)Initialize a spell slot’s persistent fields.Installs ui_spell_slot_pane_vtable.
Darkages.exe:0x004594A0ui_hierarchy_get_nodevoid *__thiscall(void *, int)Return array + index * stride after bounds checks.Confirms unaligned packed-node addressing.
Darkages.exe:0x004876C4ui_local_user_dtorvoid __thiscall(struct ui_local_user *)Destroy the local-user Pane and owned state.Clears ui_local_user_singleton; the game-pane teardown path clears the owning ui_local_user root.
Darkages.exe:0x00487700ui_local_user_ctorstruct ui_local_user *__thiscall(struct ui_local_user *, void *)Construct and publish the 0x776C-byte local-user Pane.Initializes the movement sequence and timing fields.
Darkages.exe:0x00487860ui_get_local_userstruct ui_local_user *__cdecl(void)Return the active local-user pointer.Supplies the raw timing sample to the lag indicator update.
Darkages.exe:0x004884E4net_c_send_movevoid __thiscall(struct ui_local_user *, uint8_t)Build and queue CMove while starting its timing sample.Increments +0x6F28 and overwrites +0x6F2C.
Darkages.exe:0x004889F0ui_local_user_handle_server_packetint __thiscall(struct ui_local_user *, void *)Dispatch local-user server actions.Routes action 0x0B to ui_local_user_handle_smove.
Darkages.exe:0x004897C4ui_local_user_handle_smoveint __thiscall(struct ui_local_user *, const uint8_t *)Parse SMove, record a raw timing sample, and process movement acknowledgment or correction.Calls the lag indicator update after timeGetTime - last_move_send_tick.
Darkages.exe:0x00493240ui_pane_get_bound_rectvoid __thiscall(void *, int32_t *)Return Pane bounds translated by its relative origin.Reads local rectangle +0x38 and point +0xA8.
Darkages.exe:0x004932B0ui_pane_set_bound_rectvoid __thiscall(void *, const int32_t *)Split input bounds into relative origin and local rectangle.Vtable slot +0x24; called during Screen insertion.
Darkages.exe:0x00498F40ui_screen_add_paneestablished __thiscall add methodInsert a pane and 0x34-byte payload into Screen.Copies visibility and pane flags into the node.
Darkages.exe:0x004993C4ui_screen_find_pane_nodeestablished recursive __thiscall searchFind a pane in Screen.Traverses nested packed child lists.
Darkages.exe:0x00499ED0ui_screen_get_pane_originestablished __thiscall queryAccumulate a pane’s screen origin.Uses Screen parent relationships.

Pane Virtual Table Inventory

This appendix lists the 174 virtual tables confirmed to use the Pane layout in the analyzed client. The initial scan found 173 tables by locating data references to ui_pane_register_events at virtual slot +0x30, then confirming the adjacent Screen, unregister, and handler slots. GameButtonsPane was added separately because it overrides the registration slots but preserves the surrounding Pane layout. The result includes full panes, dialogs, controls, subclass variants, and temporary construction or destruction tables. It is not a claim that the client can display 174 independent windows at once.

No usable Microsoft C++ class RTTI or type-descriptor names were found for this family. The virtual-table address is therefore the reliable runtime discriminator. Each object begins with one of these table pointers while that implementation is active.

The linked code family is included only where constructor, destructor, handler, or diagnostic data flow associates the table with that implementation unit. It does not by itself assert an original C++ class name. Unassigned means that the common Pane layout is established but neither the owning implementation unit nor class has met the naming threshold.

Confirmed friendly class names

The following names meet the stronger class threshold. Each name is connected to its exact vtable by a direct table installation, a class-specific virtual stored in that table, or both. Diagnostic text is supporting evidence only after that instruction-level connection is established.

Event coverage uses M for mouse Event types 0 through 7, K for key-down Event type 8, S for socket Event type 9, and T for the separate timer callback slot. M1, for example, means mouse subtype 1. Default means the slot returns without class-specific behavior. The full handler addresses remain in the complete inventory below.

Friendly classVtable and current IDA nameDirect confirming evidenceConcrete Event-facing behavior
AggrementDialogPaneDarkages.exe:0x005002C0
ui_aggrement_dialog_pane_vtable
Destructor diagnostic plus direct table installation. The spelling Aggrement is the binary’s spelling.Generic Dialog M/K; default S/T.
BackPaneDarkages.exe:0x00500700
ui_background_pane_vtable
BackPane.cpp, the gpBackPane diagnostic, and ui_background_pane_ctor. Documentation uses BackgroundPane for its role.M1; S 0x49; T 0,1,2.
BulletinSessionDarkages.exe:0x00501E60
ui_bulletin_session_vtable
Constructor and destructor diagnostics, direct table installation, and static root publication.Custom M/K; S 0x31; default T.
BulletinDialogPaneDarkages.exe:0x00501EC0
ui_bulletin_dialog_pane_vtable
Constructor diagnostic and ui_bulletin_dialog_pane_register at class vslot +0x60.Adds M1/M3 to Dialog behavior; custom K; S 0x31; default T.
ArticleListDialogDarkages.exe:0x00501F40
ui_article_list_dialog_vtable
Constructor diagnostic and ui_article_list_dialog_start_new_article_editing at +0x4C.Adds M1/M3; custom K; S 0x31; default T.
MailListDialogDarkages.exe:0x00501FC0
ui_mail_list_dialog_vtable
Constructor diagnostic and class-specific key and completion handlers.Adds M1/M3; custom K; S 0x31; default T.
NewArticleDialogDarkages.exe:0x005020C0
ui_new_article_dialog_vtable
Constructor diagnostic and ui_new_article_dialog_handle_control_click at +0x4C.Adds M1/M3; K Tab/Enter/Escape; S 0x31; default T.
NewMailDialogDarkages.exe:0x00502140
ui_new_mail_dialog_vtable
Constructor diagnostic and ui_new_mail_dialog_handle_control_click at +0x4C.Adds M1/M3; K Tab/Enter/Escape; S 0x31; default T.
BoardListDialogDarkages.exe:0x005021C0
ui_board_list_dialog_vtable
Constructor diagnostics for its title, board count, controls, and completed object.Adds M1/M3; shared Bulletin K; S 0x31; custom T.
ArticleDialogDarkages.exe:0x00502240
ui_article_dialog_vtable
Constructor diagnostic and directly installed table.Adds M1/M3; custom K; S 0x31; default T.
MailDialogDarkages.exe:0x005022C0
ui_mail_dialog_vtable
Constructor diagnostic and directly installed table.Adds M1/M3; custom K; S 0x31; default T.
ArticleListPaneDarkages.exe:0x005023E0
ui_article_list_pane_vtable
Constructor diagnostic and ui_article_list_pane_draw_cell at +0x84.Scrollable M/K; default S/T.
MailListPaneDarkages.exe:0x00502580
ui_mail_list_pane_vtable
Constructor diagnostic plus selection-double-click and DrawCell virtuals at +0x80/+0x84.Scrollable M/K; default S/T.
LegendDialogPaneDarkages.exe:0x00506C80
ui_legend_dialog_pane_vtable
ui_legend_dialog_pane_ctor installs this table on the outer dialog. Its separately allocated inner object uses 0x00506BE0 and remains unnamed.Adds M1/M4 to Dialog behavior; generic Dialog K; default S/T.
Lag indicator paneDarkages.exe:0x00509C20
ui_lag_indicator_pane_vtable
ui_lag_indicator_pane_ctor directly installs the table, publishes a static root, and provides the statcon.epf draw method at +0x48.Default M/K/S/T; Draw at +0x48.
GameButtonsPaneDarkages.exe:0x00510300
ui_game_buttons_pane_vtable
GameButtonsPane.cpp, direct installation by ui_game_buttons_pane_ctor, and the content-selection handlers in its Event slots.Custom M/K/S/T.
HumanImageControlPaneDarkages.exe:0x005134E0
ui_human_image_control_pane_vtable
ui_human_image_control_pane_draw at vslot +0x48 and its class diagnostic.Control M/K; default S/T; Draw at +0x48.
MainMenuPaneDarkages.exe:0x005177C0
ui_main_menu_pane_vtable
ui_main_menu_pane_ctor, static root publication, and class diagnostics.M0/M3; custom K; S 0x00/0x02/0x03/0x0A; default T.
MapPaneDarkages.exe:0x00519280
ui_map_pane_vtable
ui_map_pane_ctor, static root publication, and multiple class-specific virtuals and diagnostics.Custom M/K; 31 S actions; T 0..4.
ItemListPaneDarkages.exe:0x0051B6C0
ui_item_list_pane_vtable
ui_item_list_pane_draw_cell at +0x84 and its class diagnostic.Scrollable M/K; default S/T; DrawCell at +0x84.
MonsterImageControlPaneDarkages.exe:0x0051BE60
ui_monster_image_control_pane_vtable
ui_monster_image_control_pane_draw at +0x48 and its class diagnostic.Control M/K; default S/T; Draw at +0x48.
QuestionMessageDialogDarkages.exe:0x0051C0C0
ui_question_message_dialog_vtable
ui_question_message_dialog_send_answer at +0x74 and matching class diagnostics.Adds M1/M3 to Dialog behavior; generic Dialog K; default S/T.
QuestionMessageFaceDialogDarkages.exe:0x0051C140
ui_question_message_face_dialog_vtable
Class-specific user-dialog virtual at +0x64 and ui_question_message_face_dialog_send_answer at +0x74.Adds M1/M3 to Dialog behavior; generic Dialog K; default S/T.
MonsterPane2Darkages.exe:0x0051D700
ui_monster_pane2_vtable
ui_monster_pane2_start_move at +0x64 and its class diagnostics.Custom M/S/T; base K.
NewUser local-user paneDarkages.exe:0x0051E980
ui_local_user_vtable
NewUser.cpp, direct table installation by ui_local_user_ctor, its class-level singleton publication, and the separate owning game-pane root.Custom M/K/T; S 0x04/0x05/0x08/0x0B/0x0F/0x10/0x17/0x18/0x51.
EffectObjectPaneDarkages.exe:0x0051FB00
ui_effect_object_pane_vtable
ui_effect_object_pane_handle_timer at +0x44 and its method diagnostic.Custom M/S/T; base K.
OptionPaneDarkages.exe:0x00520660
ui_option_pane_vtable
OptionPane.cpp, direct table installation by ui_option_pane_ctor, and class-specific handlers in all four Event slots.Custom M/K/T; S 0x21.
PaneDarkages.exe:0x005213A0
ui_pane_vtable
ui_pane_ctor, base destructor, and shared registration layout.Base handlers are default M/K/S/T.
ScreenPaneDarkages.exe:0x00524CE0
ui_screen_pane_vtable
Persistent root construction, static publication, and Screen-specific keyboard and timer virtuals.Custom K/T; default M/S.
ServerSelectDialogPaneDarkages.exe:0x00526140
ui_server_select_dialog_pane_vtable
Constructor and destructor diagnostics plus ui_server_select_handle_mouse_event.Adds M2 to Dialog behavior; generic Dialog K; S 0x56; default T.
SpellBookDialogDarkages.exe:0x00528280
ui_spell_book_dialog_vtable
ui_spell_book_dialog_draw at +0x48 and its method diagnostic.Generic Dialog M/K; default S/T; Draw at +0x48.
SkillBookDialogDarkages.exe:0x005282E0
ui_skill_book_dialog_vtable
ui_skill_book_dialog_draw at +0x48 and its method diagnostic.Generic Dialog M/K; default S/T; Draw at +0x48.
TerminalPaneDarkages.exe:0x00529000
ui_terminal_pane_vtable
Constructor, static root publication, and class-specific bootstrap diagnostics.M1/M3; custom K; bootstrap-stream S; default T.
WeatherPaneDarkages.exe:0x0052CFA0
ui_weather_pane_vtable
ui_weather_pane_draw at +0x48 and its class diagnostics.Custom T; default M/K/S; Draw at +0x48.

The installing or restoring column contains functions with direct references to the table. It commonly includes both a constructor and a destructor that restores the derived table during cleanup. Handler slots have these established roles:

SlotRole
+0x38Mouse Event types 0 through 7
+0x3CKeyboard Event type 8
+0x40Socket-packet Event type 9
+0x44Pane timer callback
+0x48Additional class-specific handler; generic role not yet established
+0x4COptional class-specific handler; used as a completion hook by TextEdit

All addresses below are virtual addresses in Darkages.exe.

Linked code family or confirmed classVtableInstalling or restoring functionsMouse +0x38Keyboard +0x3CSocket +0x40Timer +0x44Other +0x48, +0x4C
AggrementDialogPaneDarkages.exe:0x005002C0Darkages.exe:0x0040A034
Darkages.exe:0x0040A0E0
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0040A420
BackPane.cppDarkages.exe:0x00500700Darkages.exe:0x0040A564
Darkages.exe:0x0040A5B0
ui_background_pane_handle_mouse_event
Darkages.exe:0x0040B0C0
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_handle_server_request_portrait
Darkages.exe:0x0040B3A0
ui_background_pane_handle_timer
Darkages.exe:0x0040B240
Darkages.exe:0x0040AD00
0
BalloonPane.cppDarkages.exe:0x00500E00Darkages.exe:0x0040B480ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
Darkages.exe:0x0040BB60Darkages.exe:0x0040BE70
0
BlackHole.cppDarkages.exe:0x00501640Darkages.exe:0x0040CE10
Darkages.exe:0x0040D060
Darkages.exe:0x0040D270
Darkages.exe:0x0040D2A0Darkages.exe:0x0040D5E0ui_pane_default_socket_handler
Darkages.exe:0x0040B470
Darkages.exe:0x0040DE20Darkages.exe:0x0040D940
0
BlackHole.cppDarkages.exe:0x005016A0Darkages.exe:0x0040CE50
Darkages.exe:0x0040E010
Darkages.exe:0x0040E1F0Darkages.exe:0x0040E210ui_pane_default_socket_handler
Darkages.exe:0x0040B470
Darkages.exe:0x0040E360Darkages.exe:0x0040E240
0
BulletinSessionDarkages.exe:0x00501E60ui_bulletin_session_deleting_dtor
Darkages.exe:0x0040E660
ui_bulletin_session_ctor
Darkages.exe:0x0040EB10
Darkages.exe:0x00413930
Darkages.exe:0x0040EAD0Darkages.exe:0x0040EAC0Darkages.exe:0x00413CE0ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
0
Bulletin.cppDarkages.exe:0x00501EC0Darkages.exe:0x0040F7D4Darkages.exe:0x004142E0Darkages.exe:0x00414130Darkages.exe:0x00414260Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
nullsub_7
Darkages.exe:0x0040EA80
Bulletin.cppDarkages.exe:0x00501F40Darkages.exe:0x0040FC24Darkages.exe:0x004142E0Darkages.exe:0x00415A10Darkages.exe:0x00414260Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00414BD0
Bulletin.cppDarkages.exe:0x00501FC0Darkages.exe:0x00411C24Darkages.exe:0x004142E0Darkages.exe:0x004184C0Darkages.exe:0x00414260Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00419390
Bulletin.cppDarkages.exe:0x00502040Darkages.exe:0x00415BF4Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0042D1E0
Bulletin.cppDarkages.exe:0x005020C0Darkages.exe:0x00415044Darkages.exe:0x004142E0Darkages.exe:0x004181A0Darkages.exe:0x00414260Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00417E90
Bulletin.cppDarkages.exe:0x00502140Darkages.exe:0x00418D74Darkages.exe:0x004142E0Darkages.exe:0x0041AE70Darkages.exe:0x00414260Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0041AA80
Bulletin.cppDarkages.exe:0x005021C0Darkages.exe:0x0040F1E4Darkages.exe:0x004142E0Darkages.exe:0x00414130Darkages.exe:0x00414260Darkages.exe:0x00414770Darkages.exe:0x0042B520
Darkages.exe:0x00414340
Bulletin.cppDarkages.exe:0x00502240Darkages.exe:0x00410DC4Darkages.exe:0x004142E0Darkages.exe:0x00416E60Darkages.exe:0x00414260Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00417180
Bulletin.cppDarkages.exe:0x005022C0Darkages.exe:0x00412D24Darkages.exe:0x004142E0Darkages.exe:0x0041A3B0Darkages.exe:0x00414260Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0041A760
Bulletin.cppDarkages.exe:0x00502340Darkages.exe:0x0040F834Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0045A4C0
Bulletin.cppDarkages.exe:0x005023E0Darkages.exe:0x004107B4Darkages.exe:0x00416E10Darkages.exe:0x00416DB0Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0045A4C0
Bulletin.cppDarkages.exe:0x00502480Darkages.exe:0x004166E4Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0042D1E0
Bulletin.cppDarkages.exe:0x00502500Darkages.exe:0x0041C000Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0042D1E0
Bulletin.cppDarkages.exe:0x00502580Darkages.exe:0x00412714Darkages.exe:0x0041A310Darkages.exe:0x0041A360Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0045A4C0
Bulletin.cppDarkages.exe:0x00502620Darkages.exe:0x0041A960Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0042D1E0
Bulletin.cppDarkages.exe:0x005026A0Darkages.exe:0x0041BE70Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0042D1E0
CharacterLegend.cppDarkages.exe:0x00506BE0Darkages.exe:0x0041C230
Darkages.exe:0x0041C2E0
Darkages.exe:0x0041C4D0
Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0045A4C0
CharacterLegend.cppDarkages.exe:0x00506C80Darkages.exe:0x0041C2A4
Darkages.exe:0x0041C4D0
Darkages.exe:0x0041C770Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0041C750
ChattingPane.cppDarkages.exe:0x005070E0Darkages.exe:0x0041C924
Darkages.exe:0x0041C9A0
Darkages.exe:0x004A1680Darkages.exe:0x0041C9F0Darkages.exe:0x0041CCA0ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x004B5FA0
Config.cppDarkages.exe:0x00508400Darkages.exe:0x0041E6A0ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x004234E0ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
0
Lag indicator paneDarkages.exe:0x00509C20
ui_lag_indicator_pane_vtable
ui_lag_indicator_pane_dtor
Darkages.exe:0x00423984
ui_lag_indicator_pane_ctor
Darkages.exe:0x004239D0
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
ui_lag_indicator_pane_draw
Darkages.exe:0x00423AA0
0
Container.cppDarkages.exe:0x00509F80Darkages.exe:0x00423D10Darkages.exe:0x00424360Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00424330
Container.cppDarkages.exe:0x0050A000Darkages.exe:0x00423BC0
Darkages.exe:0x00423DA4
Darkages.exe:0x00424400
Darkages.exe:0x00424500
Darkages.exe:0x00424F20Darkages.exe:0x00423CD0Darkages.exe:0x00425370Darkages.exe:0x00425230Darkages.exe:0x00425280
Darkages.exe:0x00426180
Control.cppDarkages.exe:0x0050A9A0Darkages.exe:0x004262E4Darkages.exe:0x00426140Darkages.exe:0x00423CD0ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
Darkages.exe:0x00426180
Control.cppDarkages.exe:0x0050AA20Darkages.exe:0x00425E10
Darkages.exe:0x00425E84
Darkages.exe:0x00425EE4
Darkages.exe:0x00425F44
Darkages.exe:0x00426654
Darkages.exe:0x00426140Darkages.exe:0x00426490ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
Darkages.exe:0x00426180
Control.cppDarkages.exe:0x0050AAA0Darkages.exe:0x00425F94
Darkages.exe:0x00427B00
Darkages.exe:0x00427D30Darkages.exe:0x00423CD0ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004280A0
Darkages.exe:0x00426180
Control.cppDarkages.exe:0x0050AB20Darkages.exe:0x00425FF4
Darkages.exe:0x004285F0
Darkages.exe:0x004287D0Darkages.exe:0x004287F0Darkages.exe:0x00428810Darkages.exe:0x00428830Darkages.exe:0x00428B00
Darkages.exe:0x00426180
Control.cppDarkages.exe:0x0050ABA0Darkages.exe:0x00426054
Darkages.exe:0x00428B60
Darkages.exe:0x00429170Darkages.exe:0x00429190ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004293F0
Darkages.exe:0x00426180
Control.cppDarkages.exe:0x0050AC20Darkages.exe:0x00425DD0
Darkages.exe:0x00426200
Darkages.exe:0x00426140Darkages.exe:0x00423CD0ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00426360
Darkages.exe:0x00426180
Control.cppDarkages.exe:0x0050ACA0Darkages.exe:0x00425E84
Darkages.exe:0x00426550
Darkages.exe:0x00426140Darkages.exe:0x00426490ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00426C80
Darkages.exe:0x004267E0
Control.cppDarkages.exe:0x0050AD20Darkages.exe:0x00425EE4
Darkages.exe:0x00426E80
Darkages.exe:0x00426140Darkages.exe:0x00426490ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00427540
Darkages.exe:0x004270A0
Control.cppDarkages.exe:0x0050ADA0Darkages.exe:0x00427650Darkages.exe:0x00426140Darkages.exe:0x00426490ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00427A70
Darkages.exe:0x004278A0
Control.cppDarkages.exe:0x0050AE20Darkages.exe:0x004260C4
Darkages.exe:0x004294D0
Darkages.exe:0x004296B0Darkages.exe:0x004296E0ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00429710
Darkages.exe:0x00426180
Control.cppDarkages.exe:0x0050AEA0Darkages.exe:0x00426100
Darkages.exe:0x00429760
Darkages.exe:0x00426140Darkages.exe:0x00423CD0ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004297C0
Darkages.exe:0x00426180
Dialog.cppDarkages.exe:0x0050B760Darkages.exe:0x00429880
Darkages.exe:0x00429A30
Darkages.exe:0x00429BB0
Darkages.exe:0x0042C924
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
nullsub_7
Darkages.exe:0x0040EA80
Dialog.cppDarkages.exe:0x0050B7C0Darkages.exe:0x0042C0F0
Darkages.exe:0x0042CB50
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0042D1E0
DraggedPane.cppDarkages.exe:0x0050BF80Darkages.exe:0x0042D454
Darkages.exe:0x0042D4A0
Darkages.exe:0x0042D530ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
nullsub_18
Darkages.exe:0x0042DB00
EquipPane.cppDarkages.exe:0x0050C3A0Darkages.exe:0x0042DB10
Darkages.exe:0x0042DC20
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
Darkages.exe:0x0042DE60Darkages.exe:0x0042DDE0
0
EquipPane.cppDarkages.exe:0x0050C6C0Darkages.exe:0x0042E0E4
Darkages.exe:0x0042E1A0
Darkages.exe:0x0042EF40Darkages.exe:0x0042AED0Darkages.exe:0x0042EAF0Darkages.exe:0x0042EAA0Darkages.exe:0x0042F200
Darkages.exe:0x0042F030
EquipPane.cppDarkages.exe:0x0050C720Darkages.exe:0x0042E074
Darkages.exe:0x0042E9A4
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
Darkages.exe:0x0042F930Darkages.exe:0x0042F960
0
EquipPane.cppDarkages.exe:0x0050C780Darkages.exe:0x0042E164
Darkages.exe:0x0042FA90
Darkages.exe:0x00430500Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x00430730
Darkages.exe:0x004305A0
Exchange.cppDarkages.exe:0x0050EA00Darkages.exe:0x00435174
Darkages.exe:0x004352C0
Darkages.exe:0x0042A190Darkages.exe:0x00435DC0Darkages.exe:0x00435EA0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00435980
Exchange.cppDarkages.exe:0x0050EA60Darkages.exe:0x004352C0Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0045A4C0
Exchange.cppDarkages.exe:0x0050EB00Darkages.exe:0x00435294
Darkages.exe:0x00435FC4
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x00437C70Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00437920
Exchange.cppDarkages.exe:0x0050EB60Darkages.exe:0x00435204
Darkages.exe:0x00436D50
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x004374F0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x004370C0
Exchange.cppDarkages.exe:0x0050EBC0Darkages.exe:0x00436D50
Darkages.exe:0x00437580
Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0045A4C0
FieldMap.cppDarkages.exe:0x0050F8E0Darkages.exe:0x004387E0
Darkages.exe:0x00439240
Darkages.exe:0x004392D0Darkages.exe:0x004387B0Darkages.exe:0x004387C0Darkages.exe:0x004387D0Darkages.exe:0x00439550
0
GameButtonsPaneDarkages.exe:0x00510300ui_game_buttons_pane_dtor
Darkages.exe:0x0043A274
ui_game_buttons_pane_ctor
Darkages.exe:0x0043A5F0
Darkages.exe:0x0043BFB0ui_game_buttons_handle_key_event
Darkages.exe:0x0043CF20
Darkages.exe:0x0043D650Darkages.exe:0x0043D7B0Darkages.exe:0x0043D8D0
0
GameButtonsPane.cppDarkages.exe:0x00510360Darkages.exe:0x00439FF4
Darkages.exe:0x0043AF04
Darkages.exe:0x0043FE80ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x0043F420Darkages.exe:0x004402A0Darkages.exe:0x004402F0
0
GameButtonsPane.cppDarkages.exe:0x005103C0Darkages.exe:0x00439D00
Darkages.exe:0x0043A9A4
Darkages.exe:0x00440AC0
Darkages.exe:0x00440BA0
Darkages.exe:0x00441760ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x00441850ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00442140
0
GameButtonsPane.cppDarkages.exe:0x00510420Darkages.exe:0x00439DE0
Darkages.exe:0x0043A9A4
Darkages.exe:0x00442230
Darkages.exe:0x00442300
Darkages.exe:0x004428A0ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x00442990Darkages.exe:0x00443190Darkages.exe:0x00443290
0
GameButtonsPane.cppDarkages.exe:0x00510480Darkages.exe:0x00439EC0
Darkages.exe:0x0043A9A4
Darkages.exe:0x00443340
Darkages.exe:0x00443440
Darkages.exe:0x00443A20ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x00443B10Darkages.exe:0x004443C0Darkages.exe:0x004444C0
0
GameButtonsPane.cppDarkages.exe:0x005104E0Darkages.exe:0x00444570Darkages.exe:0x004A1680Darkages.exe:0x0043A5D0Darkages.exe:0x00444690ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x004B5FA0
GameButtonsPane.cppDarkages.exe:0x00510560Darkages.exe:0x0043A164
Darkages.exe:0x0043B2B4
Darkages.exe:0x0043DE30Darkages.exe:0x0043E070ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x0043E3B0
0
GameButtonsPane.cppDarkages.exe:0x005105C0Darkages.exe:0x0043A200
Darkages.exe:0x0043A5F0
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
Darkages.exe:0x00444E40Darkages.exe:0x00444EB0
0
GameButtonsPane.cppDarkages.exe:0x00510620Darkages.exe:0x0043A1D4
Darkages.exe:0x0043A5F0
Darkages.exe:0x0043E7A0
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x0043E8B0Darkages.exe:0x0043EC30Darkages.exe:0x0043EC70
0
GameButtonsPane.cppDarkages.exe:0x00510680Darkages.exe:0x0043B1E4Darkages.exe:0x00444B00ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00444D80
0
GameButtonsPane.cppDarkages.exe:0x005106E0Darkages.exe:0x0043A4E0
Darkages.exe:0x0043B374
Darkages.exe:0x00444F50
Darkages.exe:0x0042A190Darkages.exe:0x004450A0Darkages.exe:0x00445570Darkages.exe:0x004455E0Darkages.exe:0x00445260
Darkages.exe:0x00444FD0
GameButtonsPane.cppDarkages.exe:0x00510740Darkages.exe:0x0043A594
Darkages.exe:0x0043B694
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x00445EC0ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00445ED0
0
HumanPane.cppDarkages.exe:0x005134E0Darkages.exe:0x0044FD10Darkages.exe:0x00426140Darkages.exe:0x00423CD0ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x0044FD40
Darkages.exe:0x00426180
HumanPane.cppDarkages.exe:0x005138E0Darkages.exe:0x0044FE60Darkages.exe:0x0048EC60ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x0044FE20Darkages.exe:0x004503A0Darkages.exe:0x004506B0
Darkages.exe:0x0048E3D0
ItemPane.cppDarkages.exe:0x005146A0Darkages.exe:0x004536F4Darkages.exe:0x00452BB0ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x00452BC0Darkages.exe:0x00452BD0Darkages.exe:0x00452C80
0
ItemPane.cppDarkages.exe:0x00514700Darkages.exe:0x00453684Darkages.exe:0x00453A20ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x00452BE0Darkages.exe:0x00452D00Darkages.exe:0x00452C80
0
ItemPane.cppDarkages.exe:0x00514760Darkages.exe:0x00453640Darkages.exe:0x00453790ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x00452BE0Darkages.exe:0x00452D00Darkages.exe:0x00452C80
0
ItemPane.cppDarkages.exe:0x005147C0Darkages.exe:0x00456AF0Darkages.exe:0x00453E60ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x00452BF0Darkages.exe:0x00454220Darkages.exe:0x00454310
Darkages.exe:0x00452B90
ItemPane.cppDarkages.exe:0x00514820Darkages.exe:0x00456B90Darkages.exe:0x004544B0ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x00452C00ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00454CE0
0
ItemPane.cppDarkages.exe:0x00514880Darkages.exe:0x00456C70Darkages.exe:0x00455040ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x00452C10Darkages.exe:0x004560B0Darkages.exe:0x00456260
0
ItemPane.cppDarkages.exe:0x005148E0Darkages.exe:0x00453790
Darkages.exe:0x00453A24
Darkages.exe:0x00453D40
Darkages.exe:0x0042D530ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
Darkages.exe:0x00457270
ItemPane.cppDarkages.exe:0x00514940Darkages.exe:0x004529F0
Darkages.exe:0x00452DF4
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x004533E0
ItemPane.cppDarkages.exe:0x005149A0Darkages.exe:0x00452A30
Darkages.exe:0x004530C4
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00453520
ItemPane.cppDarkages.exe:0x00514A00Darkages.exe:0x00453E60Darkages.exe:0x0042D530ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
Darkages.exe:0x004570B0
ItemPane.cppDarkages.exe:0x00514A60Darkages.exe:0x004544B0Darkages.exe:0x0042D530ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
Darkages.exe:0x00456F10
ItemPane.cppDarkages.exe:0x00514AC0Darkages.exe:0x00456074Darkages.exe:0x0042D530ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
Darkages.exe:0x00456D60
ItemPane.cppDarkages.exe:0x00514B20Darkages.exe:0x00455EC4Darkages.exe:0x004BE100Darkages.exe:0x004BE6F0Darkages.exe:0x004BE1B0Darkages.exe:0x004BE260Darkages.exe:0x004BE2C0
Darkages.exe:0x00456470
ItemPane.cppDarkages.exe:0x00514B80Darkages.exe:0x00455604Darkages.exe:0x004BE100Darkages.exe:0x004BE640Darkages.exe:0x004BE1B0Darkages.exe:0x004BE260Darkages.exe:0x004BE2C0
Darkages.exe:0x00456560
ItemPane.cppDarkages.exe:0x00514BE0Darkages.exe:0x00452B64
Darkages.exe:0x00456860
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x004568B0Darkages.exe:0x00456940nullsub_6
Darkages.exe:0x0040EA60
0
ListPane.cppDarkages.exe:0x00516260Darkages.exe:0x0045A080
Darkages.exe:0x0045A0C0
Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0045A4C0
MainMenu.cppDarkages.exe:0x005177C0Darkages.exe:0x0045D754
Darkages.exe:0x0045D930
ui_main_menu_handle_mouse_event
Darkages.exe:0x0045D9B0
ui_main_menu_handle_key_event
Darkages.exe:0x0045F480
ui_main_menu_handle_server_packet
Darkages.exe:0x0045F780
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00460750
0
MainMenu.cppDarkages.exe:0x00517820Darkages.exe:0x0045D7D0
Darkages.exe:0x0045E934
Darkages.exe:0x0042A190Darkages.exe:0x004629C0Darkages.exe:0x0045D8A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00462A60
MainMenu.cppDarkages.exe:0x005178A0Darkages.exe:0x0045D790
Darkages.exe:0x0045DD14
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x00461080Darkages.exe:0x00461D20Darkages.exe:0x00460B80
Darkages.exe:0x00461E80
MainMenu.cppDarkages.exe:0x00517900Darkages.exe:0x0045D820
Darkages.exe:0x0045F004
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x00463A60Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00463210
MainMenu.cppDarkages.exe:0x00517960Darkages.exe:0x00463594Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x00464300Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00464060
MapPane.cppDarkages.exe:0x00519220Darkages.exe:0x00465530
Darkages.exe:0x004655A4
Darkages.exe:0x004657C0
Darkages.exe:0x00465C84
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
0
MapPane.cppDarkages.exe:0x00519280Darkages.exe:0x004655A4
Darkages.exe:0x004658C0
ui_map_handle_mouse_event
Darkages.exe:0x00467360
ui_map_handle_key_event
Darkages.exe:0x00467320
ui_map_dispatch_server_packet
Darkages.exe:0x00468A90
ui_map_handle_timer
Darkages.exe:0x0046CB20
Darkages.exe:0x0046D0D0
0
MapPane.cppDarkages.exe:0x005192E0Darkages.exe:0x004690F0Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00470410
MapView.cppDarkages.exe:0x0051A420Darkages.exe:0x004704C4
Darkages.exe:0x00470770
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
Darkages.exe:0x00470D00ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00470810
0
Merchant.cppDarkages.exe:0x0051AD20Darkages.exe:0x004716F0
Darkages.exe:0x00471D10
Darkages.exe:0x00479240
Darkages.exe:0x00471C90Darkages.exe:0x00479470Darkages.exe:0x00479480ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
0
Merchant.cppDarkages.exe:0x0051AD80Darkages.exe:0x00471CB4Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
nullsub_7
Darkages.exe:0x0040EA80
Merchant.cppDarkages.exe:0x0051AE00Darkages.exe:0x00472AA4Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0047BBF0
Merchant.cppDarkages.exe:0x0051AE80Darkages.exe:0x004729A4Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0047C190
Merchant.cppDarkages.exe:0x0051AF00Darkages.exe:0x004747A4Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0047C630
Darkages.exe:0x0047C2F0
Merchant.cppDarkages.exe:0x0051AF80Darkages.exe:0x00471C64
Darkages.exe:0x0047A984
Darkages.exe:0x00426140Darkages.exe:0x00423CD0ui_pane_default_socket_handler
Darkages.exe:0x0040B470
Darkages.exe:0x0047D7F0Darkages.exe:0x0047D870
Darkages.exe:0x00426180
Merchant.cppDarkages.exe:0x0051B000Darkages.exe:0x00472074Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0047B8E0
Darkages.exe:0x0047B610
Merchant.cppDarkages.exe:0x0051B080Darkages.exe:0x00473544Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0047B8E0
Darkages.exe:0x0047B610
Merchant.cppDarkages.exe:0x0051B100Darkages.exe:0x00473CE4Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0047C190
Merchant.cppDarkages.exe:0x0051B180Darkages.exe:0x00474FB4Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0047C630
Darkages.exe:0x0047C2F0
Merchant.cppDarkages.exe:0x0051B200Darkages.exe:0x00475814Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0047C190
Merchant.cppDarkages.exe:0x0051B280Darkages.exe:0x00476284Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0047C190
Merchant.cppDarkages.exe:0x0051B300Darkages.exe:0x00476C14Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0047C190
Merchant.cppDarkages.exe:0x0051B380Darkages.exe:0x00477F14Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0047C190
Merchant.cppDarkages.exe:0x0051B400Darkages.exe:0x00477594Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0047C190
Merchant.cppDarkages.exe:0x0051B480Darkages.exe:0x004788F4Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0047C190
Merchant.cppDarkages.exe:0x0051B500Darkages.exe:0x004795A4Darkages.exe:0x0047B050Darkages.exe:0x0042AED0Darkages.exe:0x0047AFD0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0047D480
Merchant.cppDarkages.exe:0x0051B580Darkages.exe:0x00473264Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0045A4C0
Merchant.cppDarkages.exe:0x0051B620Darkages.exe:0x004745F4Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0045A4C0
Merchant.cppDarkages.exe:0x0051B6C0Darkages.exe:0x00476174Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0045A4C0
Message.cppDarkages.exe:0x0051BE60Darkages.exe:0x0047E780Darkages.exe:0x00426140Darkages.exe:0x00423CD0ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00482F30
Darkages.exe:0x00426180
Message.cppDarkages.exe:0x0051BEE0Darkages.exe:0x0047DD84Darkages.exe:0x0047DBA0Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
nullsub_7
Darkages.exe:0x0040EA80
Message.cppDarkages.exe:0x0051BF40Darkages.exe:0x0047DD50Darkages.exe:0x0047DBA0Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0047E870
Darkages.exe:0x0047DED0
Message.cppDarkages.exe:0x0051BFC0Darkages.exe:0x0047DF50
Darkages.exe:0x0047F250
Darkages.exe:0x00481650
Darkages.exe:0x00483030
Darkages.exe:0x00426140Darkages.exe:0x00423CD0ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x00483060
Darkages.exe:0x00426180
Message.cppDarkages.exe:0x0051C040Darkages.exe:0x0047EAA0Darkages.exe:0x0047DBA0Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0047EF30
Darkages.exe:0x0047DED0
Message.cppDarkages.exe:0x0051C0C0Darkages.exe:0x0047EFC0Darkages.exe:0x0047DBA0Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0047FAA0
Darkages.exe:0x0047F200
Message.cppDarkages.exe:0x0051C140Darkages.exe:0x0047FC80Darkages.exe:0x0047DBA0Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x004806C0
Darkages.exe:0x0047FEC0
Message.cppDarkages.exe:0x0051C1C0Darkages.exe:0x004808A0Darkages.exe:0x0047DBA0Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00480A20
Message.cppDarkages.exe:0x0051C240Darkages.exe:0x004812C0Darkages.exe:0x0047DBA0Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x00482070
Darkages.exe:0x00481600
Message.cppDarkages.exe:0x0051C2C0Darkages.exe:0x00482230Darkages.exe:0x0047DBA0Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x004824F0
UnassignedDarkages.exe:0x0051CAA0Darkages.exe:0x00483120
Darkages.exe:0x00483770
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x00483C30ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
0
MonsterPane2Darkages.exe:0x0051D700Darkages.exe:0x004859E4
Darkages.exe:0x00485B00
Darkages.exe:0x0048EC60ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x0044FE20Darkages.exe:0x004865A0Darkages.exe:0x00486100
Darkages.exe:0x0048E3D0
UnassignedDarkages.exe:0x0051E100Darkages.exe:0x00487320
Darkages.exe:0x00487360
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
0
NewUser local-user paneDarkages.exe:0x0051E980
ui_local_user_vtable
ui_local_user_dtor
Darkages.exe:0x004876C4
ui_local_user_ctor
Darkages.exe:0x00487700
Darkages.exe:0x00487C40Darkages.exe:0x00487C70ui_local_user_handle_server_packet
Darkages.exe:0x004889F0
Darkages.exe:0x00489980Darkages.exe:0x004506B0
Darkages.exe:0x0048E3D0
ObjectPane.cppDarkages.exe:0x0051FA40Darkages.exe:0x0048C514Darkages.exe:0x0048BFB0ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x0044FE20Darkages.exe:0x0048BFC0nullsub_35
Darkages.exe:0x0048BFD0
Darkages.exe:0x00452B90
ObjectPane.cppDarkages.exe:0x0051FAA0Darkages.exe:0x0048BC74
Darkages.exe:0x0048C160
Darkages.exe:0x0048C680ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x0048BFE0Darkages.exe:0x0048C840nullsub_35
Darkages.exe:0x0048BFD0
Darkages.exe:0x00452B90
ObjectPane.cppDarkages.exe:0x0051FB00Darkages.exe:0x0048C910Darkages.exe:0x0048BFF0ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x0048C000Darkages.exe:0x0048D2E0Darkages.exe:0x0048D4C0
Darkages.exe:0x00452B90
ObjectPane.cppDarkages.exe:0x0051FB80Darkages.exe:0x0048D600Darkages.exe:0x0048C010ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x0048C020Darkages.exe:0x0048DA40Darkages.exe:0x0048DB60
Darkages.exe:0x00452B90
ObjectPane.cppDarkages.exe:0x0051FC00Darkages.exe:0x0048BD80
Darkages.exe:0x0048DC10
Darkages.exe:0x0048C030ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x0048C040Darkages.exe:0x0048BFC0Darkages.exe:0x0048E040
Darkages.exe:0x00452B90
ObjectPane.cppDarkages.exe:0x0051FC80Darkages.exe:0x0048BDF0
Darkages.exe:0x0048E150
Darkages.exe:0x0048EC60ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x0044FE20Darkages.exe:0x0048EC50nullsub_36
Darkages.exe:0x0048C050
Darkages.exe:0x0048E3D0
OptionPaneDarkages.exe:0x00520660
ui_option_pane_vtable
ui_option_pane_deleting_dtor
Darkages.exe:0x0048F410
ui_option_pane_ctor
Darkages.exe:0x0048F600
ui_option_pane_handle_mouse_event
Darkages.exe:0x00490A80
ui_option_pane_handle_key_event
Darkages.exe:0x00490FE0
ui_option_pane_handle_server_packet
Darkages.exe:0x004909E0
ui_option_pane_handle_timer
Darkages.exe:0x00490D70
Darkages.exe:0x00490E90
Darkages.exe:0x0048FC50
OptionPane.cppDarkages.exe:0x005206C0Darkages.exe:0x0048F5A4
Darkages.exe:0x0048FD50
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x004920A0Darkages.exe:0x0042B520
Darkages.exe:0x00491EB0
OptionPane.cppDarkages.exe:0x00520720Darkages.exe:0x0048F484
Darkages.exe:0x00490120
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x004913F0Darkages.exe:0x0042B520
Darkages.exe:0x004911D0
OptionPane.cppDarkages.exe:0x00520780Darkages.exe:0x0048F514
Darkages.exe:0x00490470
Darkages.exe:0x0042A190Darkages.exe:0x004917D0Darkages.exe:0x00491520Darkages.exe:0x00491D80Darkages.exe:0x0042B520
Darkages.exe:0x00491AD0
OptionPane.cppDarkages.exe:0x005207E0Darkages.exe:0x00492310Darkages.exe:0x0042A190Darkages.exe:0x0042AED0ui_exit_wait_handle_server_packet
Darkages.exe:0x004921F0
Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0042D1E0
OrbPane.cppDarkages.exe:0x00520FA0Darkages.exe:0x004925F4
Darkages.exe:0x00492734
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004929C0
0
OrbPane.cppDarkages.exe:0x00521000Darkages.exe:0x00492644
Darkages.exe:0x00492690
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
Darkages.exe:0x004927F0ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
0
Pane.cppDarkages.exe:0x005213A0Darkages.exe:0x00492A90
Darkages.exe:0x00492AD0
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
nullsub_6
Darkages.exe:0x0040EA60
0
Paper.cppDarkages.exe:0x005218C0Darkages.exe:0x004939C0Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x00493B60
ParcelIconPane.cppDarkages.exe:0x00521C60Darkages.exe:0x00494BB4
Darkages.exe:0x00494BE0
Darkages.exe:0x00494EB0Darkages.exe:0x00494DC0Darkages.exe:0x00494C60Darkages.exe:0x00494E40Darkages.exe:0x00495050
0
PatchPane.cppDarkages.exe:0x00522000Darkages.exe:0x004951D0
Darkages.exe:0x004960B0
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x004955C0Darkages.exe:0x00495E80Darkages.exe:0x0042B520
Darkages.exe:0x00495E60
PatchPane.cppDarkages.exe:0x00522060Darkages.exe:0x004955C0Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0042D1E0
PatchPane.cppDarkages.exe:0x005220E0Darkages.exe:0x00496480Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0042D1E0
UnassignedDarkages.exe:0x00523840Darkages.exe:0x00497B90Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0042D1E0
UnassignedDarkages.exe:0x005245C0Darkages.exe:0x00498CE0Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x0042D1E0
Screen.cppDarkages.exe:0x00524CE0Darkages.exe:0x00498E44
Darkages.exe:0x0049B120
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_screen_pane_handle_key_event
Darkages.exe:0x0049D2F0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_screen_pane_handle_timer
Darkages.exe:0x0049BBA0
nullsub_6
Darkages.exe:0x0040EA60
0
ScreenDimmer.cppDarkages.exe:0x005257E0Darkages.exe:0x0049F2F4
Darkages.exe:0x0049F390
Darkages.exe:0x0049F370Darkages.exe:0x0049F380ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x0049F4D0
0
ScrollablePane.cppDarkages.exe:0x00525B40Darkages.exe:0x004A0E14Darkages.exe:0x0049F7F0Darkages.exe:0x004A0670ui_pane_default_socket_handler
Darkages.exe:0x0040B470
Darkages.exe:0x004A0680Darkages.exe:0x004A0740
0
ScrollablePane.cppDarkages.exe:0x00525BA0Darkages.exe:0x0049F674
Darkages.exe:0x004A0CC0
Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0049F6D0
ServerSelect dialogDarkages.exe:0x00526140Darkages.exe:0x004A1B04
Darkages.exe:0x004A2740
ui_server_select_handle_mouse_event
Darkages.exe:0x004A2C80
Darkages.exe:0x0042AED0ui_server_select_handle_server_packet
Darkages.exe:0x004A2ED0
Darkages.exe:0x0040A0B0Darkages.exe:0x004A29F0
Darkages.exe:0x004A2AF0
SpellSkillBook.cppDarkages.exe:0x00528280Darkages.exe:0x004A9A20
Darkages.exe:0x004A9BC0
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x004AA3B0
Darkages.exe:0x004AA660
SpellSkillBook.cppDarkages.exe:0x005282E0Darkages.exe:0x004A9A60
Darkages.exe:0x004AB1D0
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x004AB8A0
Darkages.exe:0x004ABB30
Terminal.cppDarkages.exe:0x00528C60Darkages.exe:0x004AC7B0ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004AC820
0
Terminal.cppDarkages.exe:0x00529000Darkages.exe:0x004AC944
Darkages.exe:0x004ACA00
ui_terminal_handle_mouse_event
Darkages.exe:0x004ADC50
ui_terminal_handle_key_event
Darkages.exe:0x004AEF70
ui_terminal_handle_server_packet
Darkages.exe:0x004AD140
ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004AF050
0
Terminal.cppDarkages.exe:0x00529060Darkages.exe:0x004AC9C0
Darkages.exe:0x004AE044
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x004AFA90
Darkages.exe:0x004AF260
TextEdit.cppDarkages.exe:0x00529CE0Darkages.exe:0x004AFC90
Darkages.exe:0x004BD504
Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x004B5FA0
TextEdit.cppDarkages.exe:0x00529D60Darkages.exe:0x004BE510Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x004B5FA0
TextEdit.cppDarkages.exe:0x00529DE0Darkages.exe:0x004BD4B0Darkages.exe:0x004AFF50Darkages.exe:0x004AFF60Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x004B5FA0
TextEdit.cppDarkages.exe:0x00529E60Darkages.exe:0x004AFE80
Darkages.exe:0x004BE354
Darkages.exe:0x004BE100Darkages.exe:0x004BE6F0Darkages.exe:0x004BE1B0Darkages.exe:0x004BE260Darkages.exe:0x004BE2C0
nullsub_41
Darkages.exe:0x004AFF70
TextEdit.cppDarkages.exe:0x00529F00Darkages.exe:0x004BE300Darkages.exe:0x004BE100Darkages.exe:0x004BE5A0Darkages.exe:0x004BE1B0Darkages.exe:0x004BE260Darkages.exe:0x004BE2C0
nullsub_41
Darkages.exe:0x004AFF70
TextEdit.cppDarkages.exe:0x00529F60Darkages.exe:0x004BE600Darkages.exe:0x004BE100Darkages.exe:0x004BE640Darkages.exe:0x004BE1B0Darkages.exe:0x004BE260Darkages.exe:0x004BE2C0
nullsub_41
Darkages.exe:0x004AFF70
UnassignedDarkages.exe:0x0052B800Darkages.exe:0x004BF504
Darkages.exe:0x004BF670
ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
Darkages.exe:0x004BF750ui_pane_default_socket_handler
Darkages.exe:0x0040B470
Darkages.exe:0x004BF720Darkages.exe:0x004BF770
0
UserPane.cppDarkages.exe:0x0052BF20Darkages.exe:0x004C08A0Darkages.exe:0x004BE100Darkages.exe:0x004BE6F0Darkages.exe:0x004BE1B0Darkages.exe:0x004BE260Darkages.exe:0x004BE2C0
Darkages.exe:0x004C0920
UserPane.cppDarkages.exe:0x0052BF80Darkages.exe:0x004C09A0Darkages.exe:0x004BE100Darkages.exe:0x004C0A00Darkages.exe:0x004BE1B0Darkages.exe:0x004BE260Darkages.exe:0x004BE2C0
Darkages.exe:0x004C0BD0
UserPane.cppDarkages.exe:0x0052BFE0Darkages.exe:0x004C1040Darkages.exe:0x004BE100Darkages.exe:0x004BE6F0Darkages.exe:0x004BE1B0Darkages.exe:0x004BE260Darkages.exe:0x004BE2C0
Darkages.exe:0x004C10D0
UserPane.cppDarkages.exe:0x0052C040Darkages.exe:0x004C0770
Darkages.exe:0x004C11A0
Darkages.exe:0x0042A190Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x004C14A0
UserPane.cppDarkages.exe:0x0052C0A0Darkages.exe:0x004C07E4
Darkages.exe:0x004C14E0
Darkages.exe:0x004BE100Darkages.exe:0x004C15B0Darkages.exe:0x004BE1B0Darkages.exe:0x004BE260Darkages.exe:0x004BE2C0
Darkages.exe:0x004C16A0
UserPane.cppDarkages.exe:0x0052C100Darkages.exe:0x004C1724Darkages.exe:0x004BE100Darkages.exe:0x004BE6F0Darkages.exe:0x004BE1B0Darkages.exe:0x004BE260Darkages.exe:0x004BE2C0
Darkages.exe:0x004C1860
UserPane.cppDarkages.exe:0x0052C160Darkages.exe:0x004C17C4Darkages.exe:0x004BE100Darkages.exe:0x004BE6F0Darkages.exe:0x004BE1B0Darkages.exe:0x004BE260Darkages.exe:0x004BE2C0
Darkages.exe:0x004C1970
UsersList.cppDarkages.exe:0x0052CB40Darkages.exe:0x004C1A80
Darkages.exe:0x004C1B30
Darkages.exe:0x004C2650
Darkages.exe:0x004C26F0Darkages.exe:0x0042AED0Darkages.exe:0x0040A0A0Darkages.exe:0x0040A0B0Darkages.exe:0x0042B520
Darkages.exe:0x004C26B0
UsersList.cppDarkages.exe:0x0052CBC0Darkages.exe:0x004C2614Darkages.exe:0x004A1680Darkages.exe:0x004A1900Darkages.exe:0x0040EA70ui_pane_default_timer_handler
Darkages.exe:0x0040EA50
Darkages.exe:0x004A1A50
Darkages.exe:0x0045A4C0
WeatherDarkages.exe:0x0052CFA0Darkages.exe:0x004C2C60ui_pane_default_mouse_handler
Darkages.exe:0x0040B460
ui_pane_default_key_handler
Darkages.exe:0x0040A5A0
ui_pane_default_socket_handler
Darkages.exe:0x0040B470
Darkages.exe:0x004C2FF0Darkages.exe:0x004C2D90
0

Glossary

IDA name

The current descriptive identifier assigned in the local IDA database. It is a research aid and does not claim to be an original source-level symbol.

RVA

Relative virtual address. An offset from the loaded image base, distinct from a file offset and a full virtual address.

Stone client

The community or project label for the client release being analyzed. Its exact relationship to the executable’s internal version metadata remains an open identity question.

XOR

Exclusive OR, a bitwise operation sometimes used in reversible byte transformations. Its use in this client is not established yet.