resedit 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/resedit +11 -0
- data/lib/resedit.rb +17 -0
- data/lib/resedit/app/app.rb +160 -0
- data/lib/resedit/app/app_command.rb +100 -0
- data/lib/resedit/app/colorizer.rb +34 -0
- data/lib/resedit/app/font_convert.rb +54 -0
- data/lib/resedit/app/io_commands.rb +74 -0
- data/lib/resedit/app/mz_command.rb +192 -0
- data/lib/resedit/app/std_commands.rb +69 -0
- data/lib/resedit/app/text_convert.rb +60 -0
- data/lib/resedit/convert/bitconv.rb +48 -0
- data/lib/resedit/font/font.rb +82 -0
- data/lib/resedit/font/font_char.rb +49 -0
- data/lib/resedit/image/image.rb +47 -0
- data/lib/resedit/image/image_factory.rb +33 -0
- data/lib/resedit/image/png_image.rb +38 -0
- data/lib/resedit/mz/changeable.rb +248 -0
- data/lib/resedit/mz/hexwriter.rb +88 -0
- data/lib/resedit/mz/mz.rb +166 -0
- data/lib/resedit/mz/mz_body.rb +141 -0
- data/lib/resedit/mz/mz_header.rb +123 -0
- data/lib/resedit/mz/mzenv.rb +82 -0
- data/lib/resedit/text/conv_keybru.rb +84 -0
- data/lib/resedit/text/conv_table.rb +10 -0
- data/lib/resedit/text/escaper.rb +117 -0
- data/lib/resedit/text/format_text.rb +44 -0
- data/lib/resedit/text/format_xml.rb +46 -0
- data/lib/resedit/text/huffman.rb +190 -0
- data/lib/resedit/text/text.rb +73 -0
- metadata +86 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Resedit
|
4
|
+
|
5
|
+
class KeyboardRuConvertTable < ConvertTable
|
6
|
+
|
7
|
+
def initialize()
|
8
|
+
super()
|
9
|
+
@table={
|
10
|
+
'Q' => 'Й',
|
11
|
+
'W' => 'Ц',
|
12
|
+
'E' => 'У',
|
13
|
+
'R' => 'К',
|
14
|
+
'T' => 'Е',
|
15
|
+
'Y' => 'Н',
|
16
|
+
'U' => 'Г',
|
17
|
+
'I' => 'Ш',
|
18
|
+
'O' => 'Щ',
|
19
|
+
'P' => 'З',
|
20
|
+
'{' => 'Х',
|
21
|
+
'}' => 'Ъ',
|
22
|
+
'A' => 'Ф',
|
23
|
+
'S' => 'Ы',
|
24
|
+
'D' => 'В',
|
25
|
+
'F' => 'А',
|
26
|
+
'G' => 'П',
|
27
|
+
'H' => 'Р',
|
28
|
+
'J' => 'О',
|
29
|
+
'K' => 'Л',
|
30
|
+
'L' => 'Д',
|
31
|
+
':' => 'Ж',
|
32
|
+
"\"" => 'Э',
|
33
|
+
'Z' => 'Я',
|
34
|
+
'X' => 'Ч',
|
35
|
+
'C' => 'С',
|
36
|
+
'V' => 'М',
|
37
|
+
'B' => 'И',
|
38
|
+
'N' => 'Т',
|
39
|
+
'M' => 'Ь',
|
40
|
+
'<' => 'Б',
|
41
|
+
'>' => 'Ю',
|
42
|
+
|
43
|
+
'q' => 'й',
|
44
|
+
'w' => 'ц',
|
45
|
+
'e' => 'у',
|
46
|
+
'r' => 'к',
|
47
|
+
't' => 'е',
|
48
|
+
'y' => 'н',
|
49
|
+
'u' => 'г',
|
50
|
+
'i' => 'ш',
|
51
|
+
'o' => 'щ',
|
52
|
+
'p' => 'з',
|
53
|
+
'[' => 'х',
|
54
|
+
']' => 'ъ',
|
55
|
+
'a' => 'ф',
|
56
|
+
's' => 'ы',
|
57
|
+
'd' => 'в',
|
58
|
+
'f' => 'а',
|
59
|
+
'g' => 'п',
|
60
|
+
'h' => 'р',
|
61
|
+
'j' => 'о',
|
62
|
+
'k' => 'л',
|
63
|
+
'l' => 'д',
|
64
|
+
';' => 'ж',
|
65
|
+
"\'" => 'э',
|
66
|
+
'z' => 'я',
|
67
|
+
'x' => 'ч',
|
68
|
+
'c' => 'с',
|
69
|
+
'v' => 'м',
|
70
|
+
'b' => 'и',
|
71
|
+
'n' => 'т',
|
72
|
+
'm' => 'ь',
|
73
|
+
',' => 'б',
|
74
|
+
'.' => 'ю',
|
75
|
+
|
76
|
+
'~' => 'Ё',
|
77
|
+
'`' => 'ё'
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Resedit
|
2
|
+
|
3
|
+
class TextEscaper
|
4
|
+
|
5
|
+
STD_TABLE = {0x5C=>"\\\\", 0x0A=>"\\n", 0x0D=>"\\r", 0x09=>"\\t", 0x07=>"\\a", 0x08=>"\\b", 0x0C=>"\\f", 0x0B=>"\\v", 0x1B=>"\\e"}
|
6
|
+
|
7
|
+
def escape(line)
|
8
|
+
out=""
|
9
|
+
line.each_byte {|b|
|
10
|
+
out += _escape(b)
|
11
|
+
}
|
12
|
+
return out
|
13
|
+
end
|
14
|
+
|
15
|
+
def unescape(line); end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def tableReplace(str,tbl,numproc=true)
|
20
|
+
ntbl={}
|
21
|
+
if tbl
|
22
|
+
tbl.each{|b,v|
|
23
|
+
ntbl[v[0]]={} if !ntbl[v[0]]
|
24
|
+
ntbl[v[0]][v]=b
|
25
|
+
}
|
26
|
+
end
|
27
|
+
pos=0
|
28
|
+
res=""
|
29
|
+
while pos < str.length
|
30
|
+
val=str[pos]
|
31
|
+
inc=1
|
32
|
+
if ntbl[val]
|
33
|
+
ntbl[val].each{|v,b|
|
34
|
+
next if str.length < pos+v.length+1
|
35
|
+
if v==str[pos,v.length]
|
36
|
+
val=b.chr
|
37
|
+
inc=v.length
|
38
|
+
break
|
39
|
+
end
|
40
|
+
}
|
41
|
+
end
|
42
|
+
if inc==1 && numproc && val=="\\"
|
43
|
+
raise "Bad escape sequence: "+str if str.length < pos+5
|
44
|
+
num = str[pos+1,3]
|
45
|
+
byte = num[0].upcase=="X" ? num[1,2].to_i(16) : num.to_i
|
46
|
+
raise "Bad numeric escape "+num+": "+str if (byte==0 and num!="000" and num!="x00") || byte>255 || byte<0
|
47
|
+
val=byte.chr
|
48
|
+
inc=4
|
49
|
+
end
|
50
|
+
res += val
|
51
|
+
pos += inc
|
52
|
+
end
|
53
|
+
return res
|
54
|
+
end
|
55
|
+
|
56
|
+
def _escape(b); end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
class SlashEscaper < TextEscaper
|
61
|
+
|
62
|
+
def _escape(b)
|
63
|
+
return '\\\\' if b==0x5c
|
64
|
+
b<0x20 ? sprintf("\\x%02X", b) : b.chr
|
65
|
+
end
|
66
|
+
|
67
|
+
def unescape(line)
|
68
|
+
tableReplace(line,{0x5C=>"\\\\"})
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
class StdEscaper < SlashEscaper
|
75
|
+
|
76
|
+
def _escape(b)
|
77
|
+
STD_TABLE[b] ? STD_TABLE[b] : super(b)
|
78
|
+
end
|
79
|
+
|
80
|
+
def unescape(line)
|
81
|
+
tableReplace(line,STD_TABLE)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
class TableEscaper < TextEscaper
|
88
|
+
|
89
|
+
def initialize(table=nil, stdTable=STD_TABLE)
|
90
|
+
@table={}
|
91
|
+
if stdTable
|
92
|
+
stdTable.each {|b, e|
|
93
|
+
add(b, e)
|
94
|
+
}
|
95
|
+
end
|
96
|
+
if table
|
97
|
+
table.each {|b, e|
|
98
|
+
add(b,e)
|
99
|
+
}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def add(byte, esc)
|
104
|
+
@table[byte] = esc
|
105
|
+
end
|
106
|
+
|
107
|
+
def _escape(b)
|
108
|
+
@table[b] ? @table[b] : b.chr
|
109
|
+
end
|
110
|
+
|
111
|
+
def unescape(line)
|
112
|
+
tableReplace(line,@table,false)
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
module Resedit
|
3
|
+
|
4
|
+
class TextFormat
|
5
|
+
|
6
|
+
def initialize(encoding)
|
7
|
+
@encoding=encoding
|
8
|
+
end
|
9
|
+
|
10
|
+
def saveLines(fname, lines, meta); end
|
11
|
+
|
12
|
+
def loadLines(fname); end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class FormatTxt < TextFormat
|
17
|
+
|
18
|
+
def initialize(encoding)
|
19
|
+
super((encoding or 'cp1251'))
|
20
|
+
end
|
21
|
+
|
22
|
+
def saveLines(fname, lines, meta)
|
23
|
+
open(fname+".txt", "w:"+@encoding) {|f|
|
24
|
+
lines.each {|l|
|
25
|
+
l.force_encoding('utf-8')
|
26
|
+
f.write(l)
|
27
|
+
f.write("\r\n")
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def loadLines(fname)
|
33
|
+
lns=[]
|
34
|
+
open(fname+".txt", "r:"+@encoding+":utf-8").each_line {|line|
|
35
|
+
lns += [line.chomp]
|
36
|
+
}
|
37
|
+
lns=lns[0..-2] if lns.last == ""
|
38
|
+
return lns
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'resedit/text/format_text'
|
2
|
+
require 'builder'
|
3
|
+
require 'rexml/document'
|
4
|
+
|
5
|
+
module Resedit
|
6
|
+
|
7
|
+
class FormatXml < TextFormat
|
8
|
+
|
9
|
+
def initialize(encoding)
|
10
|
+
super('utf-8')
|
11
|
+
end
|
12
|
+
|
13
|
+
def saveLines(fname, lines, meta)
|
14
|
+
open(fname+".xml", "w:"+@encoding) {|f|
|
15
|
+
xml=Builder::XmlMarkup.new(:indent => 2 , :target=>f)
|
16
|
+
xml.instruct! :xml, :encoding => @encoding
|
17
|
+
xml.body {|b|
|
18
|
+
lines.each.with_index{|l,i|
|
19
|
+
mt = {'id' => i}
|
20
|
+
mt.update(meta[i]) if meta[i]
|
21
|
+
b.text(l, mt)
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def loadLines(fname)
|
28
|
+
hs={}
|
29
|
+
open(fname+".xml", "r:"+@encoding) {|f|
|
30
|
+
doc=REXML::Document.new(f)
|
31
|
+
doc.elements.each("body/text"){|e|
|
32
|
+
hs[e.attributes['id']] = e.text
|
33
|
+
}
|
34
|
+
}
|
35
|
+
raise "No data in xml" if !hs.length
|
36
|
+
lns=[]
|
37
|
+
for i in 0..hs.length-1
|
38
|
+
raise "Text not found: "+i if !hs[i]
|
39
|
+
lns+=[ hs[i] ]
|
40
|
+
end
|
41
|
+
return lns
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'resedit/app/app'
|
2
|
+
module Resedit
|
3
|
+
|
4
|
+
class Huffman
|
5
|
+
|
6
|
+
class Node
|
7
|
+
attr_accessor :value, :parent, :left, :right
|
8
|
+
|
9
|
+
def initialize(value=nil, parent=nil)
|
10
|
+
@value=value
|
11
|
+
end
|
12
|
+
|
13
|
+
def addNode(isLeft,value=nil)
|
14
|
+
n=Node.new(value)
|
15
|
+
if isLeft
|
16
|
+
@left=n
|
17
|
+
else
|
18
|
+
@right=n
|
19
|
+
end
|
20
|
+
return n
|
21
|
+
end
|
22
|
+
|
23
|
+
def addLeft(value=nil)
|
24
|
+
addNode(true, value)
|
25
|
+
end
|
26
|
+
|
27
|
+
def addRight(value=nil)
|
28
|
+
addNode(false, value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def getLeafs(leftS,rightS,path='')
|
32
|
+
return {path=>@value} if @value
|
33
|
+
l=@left.getLeafs(leftS, rightS, path+leftS)
|
34
|
+
r=@right.getLeafs(leftS, rightS, path+rightS)
|
35
|
+
return l.merge(r)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
class Balancer
|
42
|
+
attr_reader :huff
|
43
|
+
def initialize(huff)
|
44
|
+
@huff = huff
|
45
|
+
@tbl = {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def addData(bytes)
|
49
|
+
bytes.each_byte{|b|
|
50
|
+
@tbl[b] = 0 if !@tbl[b]
|
51
|
+
@tbl[b] += 1
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def balanceValues()
|
56
|
+
end
|
57
|
+
|
58
|
+
def balanceTree()
|
59
|
+
raise "Not implemented."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
attr_reader :tree
|
65
|
+
|
66
|
+
def initialize(zeroLeft, reverseBitsInByte=false)
|
67
|
+
@tree=Huffman::Node.new()
|
68
|
+
@zeroLeft, @reverseBits = zeroLeft,reverseBitsInByte
|
69
|
+
end
|
70
|
+
|
71
|
+
def decodeTable(recalc=false)
|
72
|
+
if @recalc || !@decodeTbl
|
73
|
+
tbl = @tree.getLeafs(@zeroLeft ? '0' : '1', @zeroLeft ? '1' : '0')
|
74
|
+
@decodeTbl = {}
|
75
|
+
tbl.each{|k,v|
|
76
|
+
@decodeTbl[k.length] = {} if !@decodeTbl[k.length]
|
77
|
+
@decodeTbl[k.length][k.to_i(2)] = v
|
78
|
+
}
|
79
|
+
end
|
80
|
+
return @decodeTbl
|
81
|
+
end
|
82
|
+
|
83
|
+
def encodeTable(recalc=false)
|
84
|
+
if @recalc || !@encodeTbl
|
85
|
+
tbl = @tree.getLeafs(@zeroLeft ? '0' : '1', @zeroLeft ? '1' : '0')
|
86
|
+
@encodeTbl = {}
|
87
|
+
tbl.each{|k,v|
|
88
|
+
@encodeTbl[v] = [k.reverse.to_i(2), k.length]
|
89
|
+
}
|
90
|
+
end
|
91
|
+
return @encodeTbl
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
def balancer()
|
96
|
+
return Huffman::Balancer.new(self)
|
97
|
+
end
|
98
|
+
|
99
|
+
def debug()
|
100
|
+
tbl=decodeTable()
|
101
|
+
info="---huffman table---\n"
|
102
|
+
bts={}
|
103
|
+
tbl.keys.sort.each{|sz|
|
104
|
+
tbl[sz].each{|k,v|
|
105
|
+
fmt = "0x%02X\t%2d\t%0" + sz.to_s + "b\n"
|
106
|
+
info += sprintf(fmt,v,sz,k)
|
107
|
+
if not bts[v]
|
108
|
+
bts[v] = 1
|
109
|
+
else
|
110
|
+
bts[v] += 1
|
111
|
+
end
|
112
|
+
}
|
113
|
+
}
|
114
|
+
info += sprintf("---%d bytes---\n",bts.length)
|
115
|
+
for i in 0..255
|
116
|
+
if !bts[i]
|
117
|
+
info+=sprintf("0x%02X - ABSENT\n", i)
|
118
|
+
else
|
119
|
+
info+=sprintf("0x%02X - %d times\n", i, bts[i]) if bts[i]!=1
|
120
|
+
end
|
121
|
+
end
|
122
|
+
App.get().logd(info)
|
123
|
+
end
|
124
|
+
|
125
|
+
def revbyte(byte)
|
126
|
+
return byte if !@reverseBits
|
127
|
+
return sprintf("%08b", byte).reverse.to_i(2)
|
128
|
+
end
|
129
|
+
|
130
|
+
def decode(bitstream, endl=0)
|
131
|
+
res = ''
|
132
|
+
tbl = decodeTable()
|
133
|
+
max = tbl.keys.max
|
134
|
+
pos = 0
|
135
|
+
byte = bitstream[pos].ord
|
136
|
+
bytelen = 8
|
137
|
+
buf = 0
|
138
|
+
buflen = 0
|
139
|
+
while true
|
140
|
+
buf <<= 1
|
141
|
+
buflen += 1
|
142
|
+
raise "Huffman decode length overflow" if buflen>max
|
143
|
+
buf |= byte & 1
|
144
|
+
byte >>= 1
|
145
|
+
bytelen -= 1
|
146
|
+
if tbl[buflen] && tbl[buflen][buf]
|
147
|
+
return res if tbl[buflen][buf] == endl
|
148
|
+
res += tbl[buflen][buf].chr
|
149
|
+
buf = 0
|
150
|
+
buflen = 0
|
151
|
+
end
|
152
|
+
|
153
|
+
next if bytelen>0
|
154
|
+
pos += 1
|
155
|
+
break if pos >= bitstream.length
|
156
|
+
bytelen = 8
|
157
|
+
byte = revbyte(bitstream[pos].ord)
|
158
|
+
end
|
159
|
+
return res
|
160
|
+
end
|
161
|
+
|
162
|
+
def encode(bytes, endl=0)
|
163
|
+
if endl != nil && bytes[-1] != endl
|
164
|
+
bytes += endl.chr
|
165
|
+
end
|
166
|
+
res = ''
|
167
|
+
tbl = encodeTable()
|
168
|
+
byte = 0
|
169
|
+
bytelen = 0
|
170
|
+
bytes.each_byte{|b|
|
171
|
+
raise sprintf("No byte in encode table: %02X", b) if !tbl[b]
|
172
|
+
val = tbl[b][0]
|
173
|
+
for i in 0..tbl[b][1]-1
|
174
|
+
byte |= ((val & 1) << bytelen)
|
175
|
+
val >>= 1
|
176
|
+
bytelen += 1
|
177
|
+
if bytelen == 8
|
178
|
+
res += revbyte(byte).chr
|
179
|
+
bytelen = 0
|
180
|
+
byte = 0
|
181
|
+
end
|
182
|
+
end
|
183
|
+
}
|
184
|
+
res += revbyte(byte).chr if bytelen > 0
|
185
|
+
return res
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|