-
Notifications
You must be signed in to change notification settings - Fork 26
/
uv_unpack
executable file
·133 lines (122 loc) · 3.36 KB
/
uv_unpack
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
#!/usr/bin/env ruby
require "pp"
require "find"
require "fileutils"
require "pathname"
class File
def self.write(path, cnt)
File.open(path, 'wb'){|fh| fh.write cnt}
end
end
class UnitVariant
def initialize(file_path)
@file_path = file_path
@data = File.open(file_path, 'rb', &:read)
@ofs = 0
fail "Not a unit_variant file" unless get(4) == "VRNT"
end
def get(size)
fail "Trying to read past end of file" if size+@ofs > @data.size
rv = @data[@ofs, size]
@ofs += size
rv
end
def get_u
get(4).unpack("V")[0]
end
def get_u2
get(2).unpack("v")[0]
end
def get_s
get(get_u2*2).unpack("v*").pack("U*")
end
def get_s256
get(256*2).unpack("v*").pack("U*").sub(/\x00*\z/, "")
end
def parse_equipment(divider)
save_ofs, @ofs = @ofs, divider
equipment = []
while @ofs < @data.size
eq = [get_s256, get_s256, get_u2]
if eq[1] == "" and eq[2] == 1
equipment << eq[0]
else
equipment << [eq[0], eq[1]]
end
end
fail "Data left at the end of file" unless @data.size == @ofs
@ofs = save_ofs
equipment
end
def fail(msg)
raise "Failure converting #{@file_path}: #{msg}"
end
def parse
@version = get_u
unless @version == 0 or @version == 2
fail "Version #{@version} found, but only supported versions are 0 (NTW) and 2 (S2TW)"
end
cat_count = get_u
fail "Wrong header size" unless get_u == (@version == 0 ? 20 : 24)
divider = get_u
if @version == 2
@extra = get_u
else
@extra = nil
end
equipment = parse_equipment(divider)
@category = []
eq_cnt_total = 0
while @ofs < divider
cat_name = get_s256
cat_num = get_u # This is not serial id always, 9 S2TW have (0..15, 18)
# raise "Expected serial category number" unless get_u == @category.size
fail "Expected 0" unless get_u == 0
eq_cnt = get_u
fail "Expected total equipment count" unless get_u == eq_cnt_total
eq_cnt_total += eq_cnt
@category << [cat_num, cat_name, *equipment.shift(eq_cnt)]
end
fail "Category count incorrect" unless cat_count == @category.size
fail "Equipment left at the end of file" unless equipment.empty?
[@version, @category]
end
def to_tsv
version, categories = parse
out = "[\n"
out << " #{version},\n"
out << " #{@extra},\n" if @extra
categories.each do |cat_num, cat_name, *equipment|
line = " [#{cat_num}, #{cat_name.inspect}"
lines_eq = equipment.map{|eq| " #{eq.inspect}" }
out << [line, *lines_eq].join(",\n")
out << "],\n"
end
out << "]\n"
end
def save(target)
File.write(target, to_tsv)
end
end
unless ARGV.size == 2
STDERR.puts "Usage: #{$0} file.unit_variant file.txt"
STDERR.puts " or: #{$0} directory_in directory_out"
exit 1
end
source = Pathname(ARGV[0])
target = Pathname(ARGV[1])
if source.directory?
sources = []
source.find do |source_file|
sources << source_file if source_file.file?
end
sources.sort.each do |source_file|
target_subdir = target + source_file.dirname.relative_path_from(source)
target_basename = source_file.basename(".unit_variant").to_s + ".txt"
target_file = target_subdir + target_basename
FileUtils.mkdir_p target_subdir
UnitVariant.new(source_file).save(target_file)
end
else
UnitVariant.new(source).save(target)
end