2 #include <ail/array.hpp>
3 #include <ail/string.hpp>
4 #include <boost/foreach.hpp>
5 #include "d2_functions.hpp"
8 class module_offset_handler
11 module_offset_handler(unsigned image_base
, unsigned module_base
):
12 image_base(image_base
),
13 module_base(module_base
)
17 template <typename type
>
18 void fix(type
& function
, unsigned offset
)
20 function
= reinterpret_cast<type
>(module_base
+ offset
- image_base
);
24 void fix
<unsigned>(unsigned & function
, unsigned offset
)
26 function
= module_base
+ offset
- image_base
;
39 d2win_base
= 0x6F8E0000,
40 d2gfx_base
= 0x6FA80000,
41 d2common_base
= 0x6FD50000,
42 d2client_base
= 0x6FAB0000,
43 d2net_base
= 0x6FBF0000,
44 bnclient_base
= 0x6FF20000;
46 bool d2client_has_been_loaded
= false;
48 unsigned const life_mana_shift
= 8;
53 unsigned player_pointer
;
54 unsigned automap_layer_address
;
56 unsigned initialise_automap_layer_address
;
60 set_font_size_type d2_set_font_size
;
61 draw_text_type d2_draw_text
;
62 get_text_width_type d2_get_text_width
;
65 draw_line_type d2_draw_line
;
68 get_unit_stat_type d2_get_unit_stat
;
69 get_level_type d2_get_level
;
70 get_layer_type d2_get_layer
;
71 add_room_data_tye d2_add_room_data
;
72 remove_room_data_type d2_remove_room_data
;
73 initialise_level_type d2_initialise_level
;
74 get_object_table_entry_type d2_get_object_table_entry
;
75 get_inventory_item_type d2_get_inventory_item
;
76 get_next_inventory_item_type d2_get_next_inventory_item
;
77 get_item_text_type d2_get_item_text
;
78 map_to_screen_coordinates_type d2_map_to_screen_coordinates
;
79 get_skill_level_type d2_get_skill_level
;
80 get_unit_state_type d2_get_unit_state
;
83 get_player_unit_type d2_get_player_unit
;
84 get_difficulty_type d2_get_difficulty
;
85 reveal_automap_room_type d2_reveal_automap_room
;
86 new_automap_cell_type d2_new_automap_cell
;
87 add_automap_cell_type d2_add_automap_cell
;
88 leave_game_type d2_leave_game
;
89 get_unit_pointer_type d2_get_unit_pointer
;
90 get_item_name_type d2_get_item_name
;
91 click_map_type d2_click_map
;
92 find_server_side_unit_type d2_find_server_side_unit
;
93 find_client_side_unit_type d2_find_client_side_unit
;
94 get_unit_owner_type d2_get_unit_owner
;
95 print_chat_text_type d2_print_chat_text
;
97 unsigned light_handler_address
;
99 unsigned automap_handler_address
;
100 unsigned automap_loop_address
;
102 unsigned get_unit_name_address
;
108 unsigned item_handler_call_address
;
114 unsigned server_side_unit_list_address
;
117 unit_is_selectable_address
,
118 unit_selection_data_address
;
121 send_packet_type d2_send_packet
;
122 receive_packet_type d2_receive_packet
;
125 unsigned server_token_address
= 0;
127 void initialise_d2win_addresses(unsigned base
)
129 module_offset_handler
offset_handler(d2win_base
, base
);
131 offset_handler
.fix(d2_set_font_size
, 0x6F8F08D0);
132 offset_handler
.fix(d2_draw_text
, 0x6F8F0890);
133 offset_handler
.fix(d2_get_text_width
, 0x6F8EFFF0);
136 void initialise_d2gfx_addresses(unsigned base
)
138 module_offset_handler
offset_handler(d2gfx_base
, base
);
140 offset_handler
.fix(d2_draw_line
, 0x6FA87CA0);
143 void initialise_d2common_addresses(unsigned base
)
145 module_offset_handler
offset_handler(d2common_base
, base
);
147 offset_handler
.fix(d2_get_unit_stat
, 0x6FD84E20);
148 offset_handler
.fix(d2_get_level
, 0x6FDC2520);
149 offset_handler
.fix(d2_get_layer
, 0x6FDC3870);
150 offset_handler
.fix(d2_add_room_data
, 0x6FDA68F0);
151 offset_handler
.fix(d2_remove_room_data
, 0x6FDA6830);
152 offset_handler
.fix(d2_initialise_level
, 0x6FDC2C40);
153 offset_handler
.fix(d2_get_object_table_entry
, 0x6FD87300);
154 offset_handler
.fix(d2_get_inventory_item
, 0x6FDB7500);
155 offset_handler
.fix(d2_get_next_inventory_item
, 0x6FDB8400);
156 offset_handler
.fix(d2_get_item_text
, 0x6FDAC980);
157 offset_handler
.fix(d2_map_to_screen_coordinates
, 0x6FDABF50);
158 offset_handler
.fix(d2_get_skill_level
, 0x6FD5E660);
159 offset_handler
.fix(d2_get_unit_state
, 0x6FDC09A0);
161 offset_handler
.fix(data_tables
, 0x6FDEB500);
164 void initialise_d2client_addresses(unsigned base
)
166 module_offset_handler
offset_handler(d2client_base
, base
);
168 offset_handler
.fix(d2_get_player_unit
, 0x6FACE490);
169 offset_handler
.fix(d2_get_difficulty
, 0x6FB29CD0);
170 offset_handler
.fix(d2_reveal_automap_room
, 0x6FAF04C0);
171 offset_handler
.fix(d2_new_automap_cell
, 0x6FAED5B0);
172 offset_handler
.fix(d2_add_automap_cell
, 0x6FAEF090);
173 offset_handler
.fix(d2_leave_game
, 0x6FB2AB00);
174 offset_handler
.fix(d2_get_unit_pointer
, 0x6FACF1C0);
175 offset_handler
.fix(d2_get_item_name
, 0x6FB5B3C0);
176 offset_handler
.fix(d2_click_map
, 0x6FB0CE80);
177 offset_handler
.fix(d2_find_server_side_unit
, 0x6FACF1C0);
178 offset_handler
.fix(d2_find_client_side_unit
, 0x6FACF1A0);
179 offset_handler
.fix(d2_get_unit_owner
, 0x6FB73160);
180 offset_handler
.fix(d2_print_chat_text
, 0x6FB21740);
182 offset_handler
.fix(roster_list
, 0x6FBCC080);
183 offset_handler
.fix(player_pointer
, 0x6FBCC3D0);
184 offset_handler
.fix(automap_layer_address
, 0x6FBCC2B4);
185 offset_handler
.fix(initialise_automap_layer_address
, 0x6FAF0650);
187 offset_handler
.fix(light_handler_address
, 0x6FB0F8F0);
188 offset_handler
.fix(automap_handler_address
, 0x6FAEF920);
189 offset_handler
.fix(automap_loop_address
, 0x6FAF0350);
190 offset_handler
.fix(get_unit_name_address
, 0x6FACF3D0);
192 offset_handler
.fix(add_unit_address1
, 0x6FACFD9B);
193 offset_handler
.fix(add_unit_address2
, 0x6FACFD9E);
195 offset_handler
.fix(item_handler_call_address
, 0x6FB48635);
197 offset_handler
.fix(mouse_x_address
, 0x6FBC21D0);
198 offset_handler
.fix(mouse_y_address
, 0x6FBC21CC);
200 offset_handler
.fix(server_side_unit_list_address
, 0x6FBCA964);
202 //offset_handler.fix(unit_is_selectable_address, 0x6FAD0010);
203 offset_handler
.fix(unit_is_selectable_address
, 0x6FAD0071);
205 //offset_handler.fix(unit_selection_data_address, 0x6FBCC1D0);
207 d2client_has_been_loaded
= true;
210 void initialise_d2net_addresses(unsigned base
)
212 module_offset_handler
offset_handler(d2net_base
, base
);
214 offset_handler
.fix(d2_send_packet
, 0x6FBF6F90);
215 offset_handler
.fix(d2_receive_packet
, 0x6FBF6510);
218 void initialise_bnclient_addresses(unsigned base
)
220 module_offset_handler
offset_handler(bnclient_base
, base
);
222 offset_handler
.fix(server_token_address
, 0x6FF3F5D8);
225 void draw_text(std::string
const & text
, int x
, int y
, unsigned colour
, bool centered
)
227 std::size_t buffer_size
= text
.size() + 1;
228 wchar_t * wide_string_data
= new wchar_t[buffer_size
];
230 std::memset(wide_string_data
, 0, buffer_size
* 2);
231 MultiByteToWideChar(0, MB_PRECOMPOSED
, text
.c_str(), text
.length(), wide_string_data
, buffer_size
);
233 unsigned font_size
= 6;
235 unsigned old_font_size
= d2_set_font_size(font_size
);
242 d2_get_text_width(wide_string_data
, &width
, &file_number
);
247 d2_draw_text(wide_string_data
, x
, y
, colour
, -1);
248 d2_set_font_size(old_font_size
);
250 delete wide_string_data
;
259 coordinate(int x
, int y
):
266 void __stdcall
draw_box(int x
, int y
, unsigned colour
)
268 bool const continuous
= true;
270 int const square_size
= 3;
272 coordinate
const coordinates
[] =
274 coordinate(- square_size
, square_size
),
275 coordinate(square_size
, square_size
),
276 coordinate(square_size
, - square_size
),
277 coordinate(- square_size
, - square_size
),
278 coordinate(- square_size
, square_size
)
287 limit
= ail::countof(coordinates
) - 1;
292 limit
= ail::countof(coordinates
);
296 for(std::size_t i
= 0; i
< limit
; i
+= increment
)
298 coordinate
const & start
= coordinates
[i
];
299 coordinate
const & end
= coordinates
[i
+ 1];
300 d2_draw_line(x
+ start
.x
, y
+ start
.y
, x
+ end
.x
, y
+ end
.y
, colour
, 0xff);
304 bool get_life_or_mana(unsigned & current
, unsigned & maximum
, unsigned stat1
, unsigned stat2
)
306 unit
* player_unit
= d2_get_player_unit();
309 current
= d2_get_unit_stat(player_unit
, stat1
, 0) >> life_mana_shift
;
310 maximum
= d2_get_unit_stat(player_unit
, stat2
, 0) >> life_mana_shift
;
314 bool get_life(unsigned & current_life
, unsigned & maximum_life
)
316 return get_life_or_mana(current_life
, maximum_life
, 6, 7);
319 bool get_mana(unsigned & current_mana
, unsigned & maximum_mana
)
321 return get_life_or_mana(current_mana
, maximum_mana
, 8, 9);
324 monster_statistics
& get_monster_statistics(std::size_t index
)
326 char * root
= *reinterpret_cast<char **>(data_tables
);
327 monster_statistics
* table
= *reinterpret_cast<monster_statistics
**>(root
+ 0xA78);
331 roster_unit
* get_player_roster(unsigned player_id
)
333 for(roster_unit
* i
= reinterpret_cast<roster_unit
*>(roster_list
); i
; i
= i
->next_roster
)
335 if(i
->unit_id
== player_id
)
343 return *reinterpret_cast<unit
**>(player_pointer
);
346 automap_layer
* get_automap_layer()
348 return *reinterpret_cast<automap_layer
**>(automap_layer_address
);
351 __declspec(naked
) automap_layer_type_2
* __fastcall
initialise_automap_layer_stub(unsigned layer_number
)
357 call initialise_automap_layer_address
363 automap_layer_type_2
* initialise_automap_layer(unsigned level_number
)
365 automap_layer_type_2
* layer
= d2_get_layer(level_number
);
368 return initialise_automap_layer_stub(layer
->layer_number
);
371 automap_cell
** get_layer_objects_pointer()
373 return &(get_automap_layer()->objects
);
376 bool get_player_level_number(unsigned & output
)
378 unit
* unit_pointer
= d2_get_player_unit();
380 //is this really necessary? Damn you, D2BS.
383 unit_pointer
->path_data_pointer
&&
384 unit_pointer
->path_data_pointer
->room_1
&&
385 unit_pointer
->path_data_pointer
->room_1
->room_2
&&
386 unit_pointer
->path_data_pointer
->room_1
->room_2
->level
389 output
= unit_pointer
->path_data_pointer
->room_1
->room_2
->level
->level_number
;
396 bool get_player_id(unsigned & output
)
398 unit
* unit_pointer
= d2_get_player_unit();
399 if(unit_pointer
== 0)
402 output
= unit_pointer
->id
;
407 wchar_t * get_unit_name(unit
* unit_pointer
)
412 mov eax
, unit_pointer
413 call get_unit_name_address
419 roster_vector
get_roster_units()
421 roster_vector output
;
423 roster_unit
* roster_pointer
= *reinterpret_cast<roster_unit
**>(roster_list
);
424 while(roster_pointer
)
426 output
.push_back(roster_unit(*roster_pointer
));
427 roster_pointer
= roster_pointer
->next_roster
;
433 bool get_name_by_id(unsigned id
, std::string
& output
)
435 roster_vector roster_units
= get_roster_units();
436 BOOST_FOREACH(roster_unit
& current_unit
, roster_units
)
438 if(current_unit
.unit_id
== id
)
440 output
= current_unit
.get_name();
448 bool get_non_empty_tp_tome_id(unsigned & output
)
450 unit
* unit_pointer
= d2_get_player_unit();
454 inventory
* inventory_pointer
= unit_pointer
->inventory_pointer
;
455 if(!inventory_pointer
)
458 for(unit
* current_item
= d2_get_inventory_item(inventory_pointer
); current_item
!= 0; current_item
= d2_get_next_inventory_item(current_item
))
460 item_data
* item_data_pointer
= current_item
->item_data_pointer
;
461 if(!item_data_pointer
)
465 if(item_data_pointer
->item_location
!= 0)
468 item_text
* item_text_pointer
= d2_get_item_text(current_item
->table_index
);
469 if(item_text_pointer
== 0)
472 if(item_text_pointer
->get_code() != "tbk")
476 unsigned count
= d2_get_unit_stat(current_item
, 70, 0);
480 output
= current_item
->id
;
487 bool player_is_in_game()
489 if(!d2client_has_been_loaded
)
492 return d2_get_player_unit() != 0;
495 bool get_item_name(unit
* input
, std::string
& name
, std::string
& special_name
)
497 wchar_t buffer
[0x100];
498 std::memset(buffer
, 0, sizeof(buffer
));
500 if(!d2_get_item_name(input
, buffer
, ail::countof(buffer
)))
503 std::string data
= wchar_to_string(buffer
);
504 string_vector tokens
= ail::tokenise(data
, "\n");
505 switch(tokens
.size())
508 error("d2_get_item_name returned an invalid item name for item " + ail::hex_string_32(reinterpret_cast<unsigned>(input
)) + ": \"" + data
+ "\"");
514 special_name
.clear();
519 special_name
= tokens
[0];
526 void move_click(int x
, int y
)
528 d2_map_to_screen_coordinates(&x
, &y
);
529 x
-= *reinterpret_cast<int *>(mouse_x_address
);
530 y
-= *reinterpret_cast<int *>(mouse_y_address
);
531 d2_click_map(0, x
, y
, 8);
534 bool get_unit_by_id(unsigned id
, unsigned type
, unit
* & output
)
536 output
= d2_find_server_side_unit(id
, type
);
540 output
= d2_find_client_side_unit(id
, type
);
545 bool get_skill_level(unsigned skill_id
, unsigned & output
)
547 unit
* unit_pointer
= d2_get_player_unit();
551 for(skill_data
* current_skill
= unit_pointer
->skill_pointer
->first_skill
; current_skill
; current_skill
= current_skill
->next_skill
)
553 if(current_skill
->skill_information_pointer
->identifier
== skill_id
)
555 output
= d2_get_skill_level(unit_pointer
, current_skill
, true);
563 bool get_minions(unsigned player_id
, std::vector
<unit
> & output
)
566 if(!get_unit_by_id(player_id
, unit_player
, unit_pointer
))
569 act_data
* act_data_pointer
= unit_pointer
->act_data_pointer
;
570 if(act_data_pointer
== 0)
573 for(room_data_type_1
* room_pointer
= act_data_pointer
->room_1
; room_pointer
; room_pointer
= room_pointer
->next_room
)
575 for(unit
* unit_pointer
= room_pointer
->first_unit
; unit_pointer
; unit_pointer
= unit_pointer
->next_list_entry
)
577 unit
& current_unit
= *unit_pointer
;
578 if(current_unit
.type
== unit_monster
&& d2_get_unit_owner(current_unit
.id
) == player_id
)
579 output
.push_back(current_unit
);
586 void print_chat_text(std::string
const & message
)
588 //write_line(ail::hex_string(message));
589 wchar_t * wide_string
= string_to_wchar(message
);
590 //write_line(ail::hex_string(std::string((char *)wide_string, message.size() * 2)));
591 d2_print_chat_text(wide_string
, 0);