-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathd2console.rb
More file actions
executable file
·166 lines (148 loc) · 5 KB
/
d2console.rb
File metadata and controls
executable file
·166 lines (148 loc) · 5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!/usr/bin/env ruby
# d2console.rb
# Copyright 2015 Bartosz Jankowski
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'ffi'
require 'win32/registry'
require 'colorize'
require 'require_all'
require 'thread'
require_relative 'd2consts'
require_relative 'd2structs'
# Setup external functions
require_relative 'bindings/Storm' # preload our hacked storm 1st
require_relative SCRIPT_DIR + '/bindings/Windows'
$client_data = ClientData.new
$hinstance = Windows.get_module_handle(NULLPTR)
DIRECT = D2FALSE
TrueCallback = Proc.new { D2TRUE }
ErrorCallback = FFI::Function.new(:void, []) do
raise D2Critical, "An critical error occured!"
end
class D2Error < StandardError
end
class D2Critical < StandardError
end
# Loads the Diablo II DLLs
# @raise D2Critical if one or more of dlls are not found or failed to load
def loads_dlls
require_relative SCRIPT_DIR + '/bindings/Fog'
require_relative SCRIPT_DIR + '/bindings/D2Sound'
require_relative SCRIPT_DIR + '/bindings/D2Lang'
require_relative SCRIPT_DIR + '/bindings/D2Gfx'
require_relative SCRIPT_DIR + '/bindings/D2Win'
end
# Read the InstallPath key from the registry
# @return [String] the diablo installation directory
def get_d2_install_path
buffer = FFI::MemoryPointer.new(256)
Storm.reg_load_string("Diablo II", "InstallPath", 0, buffer, 256)
dir = buffer.read_string
buffer.free
dir
end
# Basic setup for the game
# @note the event stuff is not necessary at all, I left it just in case
def setup_game
path = get_d2_install_path
raise "Failed to get d2 installation path" if path.empty?
Dir.chdir path
loads_dlls
Fog.set_log_prefix("D2")
Fog.set_error_handler("Diablo II", ErrorCallback, "v1.13d", D2TRUE)
event = Windows.open_event(2, D2TRUE, "DIABLO_II_OK")
if event
Windows.set_event(event)
Windows.close_handle(event)
end
$client_data[:mpq_callback] = TrueCallback # just return a true
$client_data[:window_mode] = D2TRUE # temporary force to window module
end
# returns a query interface for requested game mode
# @param mode one of [GameMode]
# @return [Interface] function table (result of QueryInteface(ClientData*))
# @raise D2Critical if mode is not handled
def get_game_mode_fn(mode)
case mode
when GameMode[:launcher]
require_relative SCRIPT_DIR + '/bindings/D2Launch'
return D2Launch.query_interface
when GameMode[:client]
require_relative SCRIPT_DIR + '/bindings/D2Client'
return D2Client.query_interface
when GameMode[:multiplayer]
require_relative SCRIPT_DIR + '/bindings/D2Multi'
return D2Multi.query_interface
else
raise D2Critical, "Unsupported game mode: " << mode
end
end
# Loads MPQs, and start DLL's QueryInterface() functions
def main_thread
Fog.set_file_options(DIRECT, D2FALSE)
Fog.set_async_data(D2TRUE)
Fog.init
begin
puts "Loading MPQs..."
raise D2Error, "Failed to load key MPQs!" unless D2Win.load_mpqs
raise D2Error, "Failed to load additional MPQs!" unless D2Win.
load_media_mpqs(NULLPTR, NULLPTR, 0, $client_data)
$client_data[:expansion] = Fog.is_expansion ? D2TRUE : D2FALSE
puts "Initing GFX..."
raise D2Error, "Failed to init gfx system" unless D2Win.
init_gfx($hinstance, VideoMode[:gdi], D2TRUE, D2TRUE)
puts "Creating a window..."
begin
raise D2Error, "Failed to create a window" unless D2Win.
create_window($client_data[:window_mode], GameRes[:res_800x600])
if $client_data[:no_sound] == 0
puts "Initing sound system..."
D2Sound.init($client_data[:expansion], D2FALSE)
end
game_mode = GameMode[:launcher]
loop do
puts "Entering the game mode: #{GameMode.key(game_mode)}"
query_interface = get_game_mode_fn(game_mode)
game_mode = query_interface[:launch].call($client_data)
break if game_mode == GameMode[:none]
end
if $client_data[:no_sound] == 0
puts "Shutting down the sound system..."
D2Sound.shutdown
end
puts "Shutting down the GFX..."
D2Win.deinit_gfx
D2Gfx.release
puts "Unloading the MPQs..."
D2Win.unload_mpqs
rescue D2Error => e
puts "GFX deinit"
D2Gfx.release
raise e
end
rescue D2Error => e
puts "MPQ deinit"
D2Win.unload_mpqs
raise e
end
end
begin
setup_game
main_thread
# return to previous directory
Dir.chdir SCRIPT_DIR
rescue
Dir.chdir SCRIPT_DIR
end