-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathicon_extractor.lua
More file actions
140 lines (117 loc) · 4.52 KB
/
Copy pathicon_extractor.lua
File metadata and controls
140 lines (117 loc) · 4.52 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
-- A few functions for extracting 32 x 32 bitmap icons from the DATs.
-- In order to allow Windower 4 to display the alpha channels correctly,
-- the header is replaced and the pixel data is decompressed in each image.
local string = require 'string'
local io = require 'io'
local math = require 'math'
local concat = table.concat
local floor = math.floor
local byte = string.byte
local char = string.char
local sub = string.sub
local file_size = '\122\16\00\00'
local reserved1 = '\00\00'
local reserved2 = '\00\00'
local starting_address = '\122\00\00\00'
local default = '\00\00\00\00'
local dib_header_size = '\108\00\00\00'
local bitmap_width = '\32\00\00\00'
local bitmap_height = '\32\00\00\00'
local n_color_planes = '\01\00'
local bits_per_pixel = '\32\00'
local compression_type = '\03\00\00\00'
local image_size = '\00\16\00\00'
local h_resolution_target = default
local v_resolution_target = default
local default_n_colors = default
local important_colors = default
local alpha_mask = '\00\00\00\255'
local red_mask = '\00\00\255\00'
local green_mask = '\00\255\00\00'
local blue_mask = '\255\00\00\00'
local colorspace = 'sRGB'
local endpoints = string.rep('\00', 36)
local red_gamma = default
local green_gamma = default
local blue_gamma = default
local header = 'BM' .. file_size .. reserved1 .. reserved2 .. starting_address
.. dib_header_size .. bitmap_width .. bitmap_height .. n_color_planes
.. bits_per_pixel .. compression_type .. image_size
.. h_resolution_target .. v_resolution_target
.. default_n_colors .. important_colors
.. red_mask .. green_mask .. blue_mask .. alpha_mask
.. colorspace .. endpoints .. red_gamma .. green_gamma .. blue_gamma
local color_lookup = {}
for i = 0, 255 do
color_lookup[char(i)] = ''
end
local extract = {}
local function index_color_table(index_as_char)
return color_lookup[index_as_char]
end
-- A mix of 32 bit color uncompressed and *color palette-indexed bitmaps
-- Offsets defined specifically for status icons
-- * some maps use this format as well, but at 512 x 512
function extract.sts_icon(n, input, out_path)
input:seek('set', n * 0x1800)
local data = input:read(0x1800)
local length = byte(data, 0x282) -- The length is technically sub(0x281, 0x284), but only 0x282 is unique
if length == 16 then -- uncompressed
data = sub(data, 0x2BE, 0x12BD)
data = string.gsub(data, '(...)\128', '%1\255') -- All of the alpha bytes are currently 0 or 0x80.
elseif length == 08 then -- color table
local color_palette = sub(data, 0x2BE, 0x6BD)
color_palette = string.gsub(color_palette, '(...)\128', '%1\255')
local n = 0
for i = 1, 1024, 4 do
color_lookup[char(n)] = sub(color_palette, i, i + 3)
n = n + 1
end
data = string.gsub(sub(data, 0x6BE, 0xABD), '(.)', index_color_table)
elseif length == 04 then -- XIVIEW
data = sub(data, 0x2BE, 0x12BD)
else
print('Unrecognized format', tostring(length))
data = sub(data, 0x2BE, 0x12BD) -- give it a shot
end
local f = io.open(out_path, 'wb')
f:write(header .. data)
f:close()
end
local encoded_to_decoded_char = {}
local encoded_byte_to_rgba = {}
local alpha_encoded_to_decoded_adjusted_char = {}
local decoded_byte_to_encoded_char = {}
for i = 0, 255 do
encoded_byte_to_rgba[i] = ''
local n = (i % 32) * 8 + floor(i / 32)
encoded_to_decoded_char[char(i)] = char(n)
decoded_byte_to_encoded_char[n] = char(i)
n = n * 2
n = n < 256 and n or 255
alpha_encoded_to_decoded_adjusted_char[char(i)] = char(n)
end
local decoder = function(a, b, c, d)
return encoded_to_decoded_char[a]..
encoded_to_decoded_char[b]..
encoded_to_decoded_char[c]..
alpha_encoded_to_decoded_adjusted_char[d]
end
-- 32 bit color palette-indexed bitmaps. Bits are rotated and must be decoded.
function extract.item_icon(n, input, out_path)
input:seek('set', n * 3072 + 701)
local data = input:read(2048)
local color_palette = string.gsub(sub(data, 1, 1024), '(.)(.)(.)(.)', decoder)
-- rather than decoding all 2048 bytes, decode only the palette and index it by encoded byte
for i = 0, 255 do
local offset = i * 4 + 1
encoded_byte_to_rgba[decoded_byte_to_encoded_char[i]] = sub(color_palette, offset, offset + 3)
end
local f = io.open(out_path, 'wb')
f:write(header .. string.gsub(sub(data, 1025, 2048), '(.)', function(a) return encoded_byte_to_rgba[a] end))
f:close()
end
-- n: number indicating which icon file to extract from the DAT
-- input: file object of open DAT e.g. input = io.open('P:ath/to/some/DAT.dat', 'rb') *don't forget b option on windows
-- out_path: string indicating file path where the image should be created
return extract