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,49 @@
|
|
1
|
+
module Resedit
|
2
|
+
|
3
|
+
class FontChar
|
4
|
+
attr_accessor :index, :data, :realWidth
|
5
|
+
|
6
|
+
def initialize(width, height, index, data=nil, realWidth=nil)
|
7
|
+
@width, @height, @index = width, height, index
|
8
|
+
@realWidth=realWidth
|
9
|
+
@data=data if (data && data.length==width*height)
|
10
|
+
end
|
11
|
+
|
12
|
+
def hasPixel(x, y)
|
13
|
+
@data[y*@width+x] != 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def draw(image, color, x, y, wColor)
|
17
|
+
for j in 0..@height-1
|
18
|
+
for i in 0..@width-1
|
19
|
+
image.setPixel(x+i, y+j, color) if hasPixel(i,j)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
if @realWidth && @realWidth<@width
|
23
|
+
image.setPixel(x+realWidth, y, wColor)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def scan(image, color, x, y, wColor)
|
28
|
+
@data=[0]*@width*@height
|
29
|
+
@realWidth = nil
|
30
|
+
_hasData = false
|
31
|
+
for j in 0..@height-1
|
32
|
+
for i in 0..@width-1
|
33
|
+
col=image.getPixel(x+i, y+j)
|
34
|
+
if col==color
|
35
|
+
@data[j*@width+i]= 1
|
36
|
+
_hasData = true
|
37
|
+
end
|
38
|
+
if col ==wColor
|
39
|
+
@realWidth = i
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
@data=nil if !_hasData
|
44
|
+
return @data!=nil
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
module Resedit
|
3
|
+
|
4
|
+
class Image
|
5
|
+
# Abstract image class
|
6
|
+
attr_accessor :width, :height
|
7
|
+
TYPE_PNG = 'png'
|
8
|
+
TYPE_BMP = 'bmp'
|
9
|
+
FORMAT_INDEXED = 0
|
10
|
+
FORMAT_32BIT = 1
|
11
|
+
|
12
|
+
|
13
|
+
def fill(color)
|
14
|
+
for j in (0..@height-1)
|
15
|
+
for i in (0..@width-1)
|
16
|
+
setPixel(i, j, color)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def hline(y, color)
|
23
|
+
for i in (0..@width-1)
|
24
|
+
setPixel(i, y, color)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def vline(x, color)
|
30
|
+
for j in (0..@height-1)
|
31
|
+
setPixel(x, j, color)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
#abstract interface
|
37
|
+
def getPixel(x, y); end
|
38
|
+
def setPixel(x, y, color); end
|
39
|
+
|
40
|
+
def save(filename); end
|
41
|
+
|
42
|
+
protected
|
43
|
+
def create(width, height, format); end
|
44
|
+
def load(filename); end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'resedit/image/png_image'
|
2
|
+
require 'resedit/image/image'
|
3
|
+
|
4
|
+
module Resedit
|
5
|
+
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def createImage(width, height, type=Image::TYPE_PNG, format=Image::FORMAT_INDEXED)
|
9
|
+
type = type[-3..-1] if type.length>3
|
10
|
+
case type
|
11
|
+
when Image::TYPE_PNG
|
12
|
+
img = PngImage.new()
|
13
|
+
else
|
14
|
+
raise "Unknown format #{type}"
|
15
|
+
end
|
16
|
+
img.create(width, height, format)
|
17
|
+
return img
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def loadImage(filename)
|
22
|
+
ext = filename[-3..-1].downcase()
|
23
|
+
case ext
|
24
|
+
when Image::TYPE_PNG
|
25
|
+
img=PngImage.new()
|
26
|
+
else
|
27
|
+
raise "Unknown file format #{filename}"
|
28
|
+
end
|
29
|
+
img.load(filename)
|
30
|
+
return img
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'resedit/image/image'
|
2
|
+
require 'chunky_png'
|
3
|
+
|
4
|
+
module Resedit
|
5
|
+
|
6
|
+
class PngImage < Image
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@img = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def getPixel(x, y)
|
13
|
+
col = @img[x, y]
|
14
|
+
return (col<<24 & 0xFFFFFFFF) | (col>>8)
|
15
|
+
end
|
16
|
+
|
17
|
+
def setPixel(x, y, color)
|
18
|
+
@img[x, y] = ((color<<8 & 0xFFFFFFFF) | (color>>24))
|
19
|
+
end
|
20
|
+
|
21
|
+
def create(width, height, format)
|
22
|
+
@width, @height = width, height
|
23
|
+
@img = ChunkyPNG::Image.new(width, height, ChunkyPNG::Color::TRANSPARENT)
|
24
|
+
end
|
25
|
+
|
26
|
+
def save(filename)
|
27
|
+
filename+='.png' if filename[-4..-1].downcase() != '.png'
|
28
|
+
@img.save(filename)
|
29
|
+
end
|
30
|
+
|
31
|
+
def load(filename)
|
32
|
+
@img = ChunkyPNG::Image.from_file(filename)
|
33
|
+
@width = @img.width
|
34
|
+
@height = @img.height
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
require 'resedit/app/colorizer'
|
2
|
+
require 'resedit/mz/hexwriter'
|
3
|
+
require 'resedit/mz/mzenv'
|
4
|
+
|
5
|
+
module Resedit
|
6
|
+
|
7
|
+
class Changeable
|
8
|
+
HOW_CHANGED = 0
|
9
|
+
HOW_ORIGINAL = 1
|
10
|
+
COL_CHANGED = Colorizer::YELLOW
|
11
|
+
COL_ORIGINAL = Colorizer::PURPLE
|
12
|
+
|
13
|
+
attr_reader :mz, :bytes, :add, :changes, :realSize, :realOfs
|
14
|
+
|
15
|
+
def initialize(mz, file, size)
|
16
|
+
@mz = mz
|
17
|
+
@bytes = file.read(size)
|
18
|
+
@realOfs = 0
|
19
|
+
@realSize = @bytes.size
|
20
|
+
@add = nil
|
21
|
+
@changes = {}
|
22
|
+
@c2 = []
|
23
|
+
@col = App::get().col
|
24
|
+
@mode = HOW_ORIGINAL
|
25
|
+
end
|
26
|
+
|
27
|
+
def readMore(file, size)
|
28
|
+
@bytes += file.read(size)
|
29
|
+
@realSize = @bytes.size
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def mode(how)
|
34
|
+
return if @mode == how
|
35
|
+
if how == HOW_CHANGED
|
36
|
+
@bytes += @add if @add
|
37
|
+
else
|
38
|
+
@add = @bytes[@realSize..-1] if @bytes.length > @realSize
|
39
|
+
@bytes = @bytes[0, @realSize]
|
40
|
+
end
|
41
|
+
@changes.each{|c,bts|
|
42
|
+
@changes[c] = @bytes[c, bts.length]
|
43
|
+
bts.each_byte{|b|
|
44
|
+
@bytes[c] = b.chr
|
45
|
+
c += 1
|
46
|
+
}
|
47
|
+
}
|
48
|
+
@mode = how
|
49
|
+
end
|
50
|
+
|
51
|
+
def append(bytes)
|
52
|
+
mode(HOW_ORIGINAL)
|
53
|
+
pos = @bytes.length + (@add ? @add.length : 0)
|
54
|
+
@add = @add ? @add + bytes : bytes
|
55
|
+
return pos
|
56
|
+
end
|
57
|
+
|
58
|
+
def removeAppend()
|
59
|
+
mode(HOW_ORIGINAL)
|
60
|
+
@add = nil
|
61
|
+
@bytes = @bytes[0,@realSize]
|
62
|
+
return true
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def changed?(ofs, size=2)
|
67
|
+
return true if ofs+size > @realSize
|
68
|
+
lower = @c2.find { |e| e < (ofs + size) }
|
69
|
+
return false if !lower
|
70
|
+
return lower + @changes[lower].length > ofs
|
71
|
+
end
|
72
|
+
|
73
|
+
def nextChange(ofs)
|
74
|
+
return ofs if changed?(ofs,1)
|
75
|
+
return @c2.reverse.find { |e| e > ofs }
|
76
|
+
end
|
77
|
+
|
78
|
+
def checkRange(ofs, size)
|
79
|
+
raise "Wrong offset: "+ofs.to_s if ofs < 0 || ofs >= @bytes.length
|
80
|
+
raise "Byte range overflow: " + ((ofs + size)-@bytes.length).to_s if ofs + size > @bytes.length
|
81
|
+
end
|
82
|
+
|
83
|
+
def bufWrite(buf, str, index)
|
84
|
+
return buf[0, index] + str + buf[index+str.length .. -1]
|
85
|
+
end
|
86
|
+
|
87
|
+
def change(ofs, bytes)
|
88
|
+
if ofs > @realSize
|
89
|
+
mode(HOW_CHANGED)
|
90
|
+
checkRange(ofs, bytes.length)
|
91
|
+
bytes.each_byte{|b|
|
92
|
+
@bytes[ofs] = b.chr
|
93
|
+
ofs += 1
|
94
|
+
}
|
95
|
+
return ofs
|
96
|
+
end
|
97
|
+
mode(HOW_ORIGINAL)
|
98
|
+
checkRange(ofs, bytes.length)
|
99
|
+
if changed?(ofs,bytes.length)
|
100
|
+
lower = @c2.find { |e| e < ofs + bytes.length }
|
101
|
+
strt = [lower, ofs].min()
|
102
|
+
en = [lower+@changes[lower].length, ofs+bytes.length].max()
|
103
|
+
buf = ("\0" * (en-strt)).force_encoding(Encoding::ASCII_8BIT)
|
104
|
+
buf = bufWrite(buf, @changes[lower], lower - strt)
|
105
|
+
buf = bufWrite(buf, bytes, ofs - strt)
|
106
|
+
@changes.delete(lower)
|
107
|
+
@c2.delete(lower)
|
108
|
+
change(strt, buf)
|
109
|
+
else
|
110
|
+
@changes[ofs] = bytes
|
111
|
+
@c2 = @changes.keys.reverse
|
112
|
+
end
|
113
|
+
return ofs
|
114
|
+
end
|
115
|
+
|
116
|
+
def revertChange(ofs)
|
117
|
+
raise sprintf("Change not found at: ") if !@changes[ofs]
|
118
|
+
mode(HOW_ORIGINAL)
|
119
|
+
@changes.delete(ofs)
|
120
|
+
@c2 = @changes.keys.reverse
|
121
|
+
return ofs
|
122
|
+
end
|
123
|
+
|
124
|
+
def revert(what)
|
125
|
+
mode(HOW_ORIGINAL)
|
126
|
+
if what=='all'
|
127
|
+
removeAppend()
|
128
|
+
@changes = {}
|
129
|
+
@c2=[]
|
130
|
+
return true
|
131
|
+
end
|
132
|
+
if what == 'append' || what==@realSize+@realOfs
|
133
|
+
removeAppend()
|
134
|
+
return true
|
135
|
+
end
|
136
|
+
return false if !@changes[what-@realOfs]
|
137
|
+
revertChange(what-@realOfs)
|
138
|
+
return true
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
def curcol() return @mode == HOW_ORIGINAL ? COL_ORIGINAL : COL_CHANGED end
|
143
|
+
|
144
|
+
def colVal(ofs, size)
|
145
|
+
fmt = "%#{size*2}.#{size*2}X"
|
146
|
+
u = size == 2 ? "v" : V
|
147
|
+
return colStr( sprintf(fmt, getData(ofs, size).unpack(u)[0]) , changed?(ofs,size))
|
148
|
+
end
|
149
|
+
|
150
|
+
def colStr(str, cond=true)
|
151
|
+
str = sprintf("%04X", str) if !str.is_a?(String)
|
152
|
+
return str if !cond
|
153
|
+
return @col.color(curcol(), str)
|
154
|
+
end
|
155
|
+
|
156
|
+
def getData(ofs, size)
|
157
|
+
checkRange(ofs, size)
|
158
|
+
return @bytes[ofs,size]
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
def parseHow(how)
|
163
|
+
return HOW_CHANGED if !how || how == HOW_CHANGED
|
164
|
+
return HOW_ORIGINAL if how == HOW_ORIGINAL
|
165
|
+
return HOW_ORIGINAL if how[0] == 'o' || how[0] == 'O'
|
166
|
+
return HOW_CHANGED
|
167
|
+
end
|
168
|
+
|
169
|
+
def print(what, how)
|
170
|
+
mode(parseHow(how))
|
171
|
+
if what=="changes"
|
172
|
+
@changes.each{|ofs,bts|
|
173
|
+
bts = getData(ofs, bts.length)
|
174
|
+
printf("%08X: %s\n", ofs+@realOfs, colStr(bts.bytes.map { |b| sprintf("%02X",b) }.join))
|
175
|
+
}
|
176
|
+
if @add
|
177
|
+
printf("%08X: %s\n", @realSize+@realOfs, colStr(@add.bytes.map { |b| sprintf("%02X",b) }.join))
|
178
|
+
end
|
179
|
+
puts
|
180
|
+
return true
|
181
|
+
end
|
182
|
+
return false
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
def hex(writer, ofs, size, how)
|
187
|
+
mode(parseHow(how))
|
188
|
+
col = curcol()
|
189
|
+
return size if ofs > @bytes.length
|
190
|
+
while size > 0
|
191
|
+
if ofs>=@realSize
|
192
|
+
sz = [size, @bytes.length - ofs].min
|
193
|
+
if sz
|
194
|
+
writer.addBytes(@bytes[ofs, sz], col)
|
195
|
+
size -= sz
|
196
|
+
end
|
197
|
+
return size
|
198
|
+
end
|
199
|
+
x = nextChange(ofs)
|
200
|
+
x = @realSize if !x
|
201
|
+
sz = [size, x-ofs].min
|
202
|
+
if sz
|
203
|
+
writer.addBytes(@bytes[ofs, sz], nil)
|
204
|
+
size -= sz
|
205
|
+
ofs += sz
|
206
|
+
end
|
207
|
+
return 0 if size == 0
|
208
|
+
if @changes[x]
|
209
|
+
sz = [size, @changes[x].length].min
|
210
|
+
if sz
|
211
|
+
writer.addBytes(@bytes[ofs, sz], col)
|
212
|
+
size -= sz
|
213
|
+
ofs += sz
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def saveData(file)
|
220
|
+
mode(HOW_CHANGED)
|
221
|
+
file.write(@bytes)
|
222
|
+
end
|
223
|
+
|
224
|
+
def saveChanges(file)
|
225
|
+
mode(HOW_CHANGED)
|
226
|
+
file.write([@realSize, @changes.length].pack('VV'))
|
227
|
+
@changes.each{|c,bts|
|
228
|
+
file.write([c, bts.length].pack('VV'))
|
229
|
+
file.write(bts)
|
230
|
+
}
|
231
|
+
end
|
232
|
+
|
233
|
+
def loadChanges(file)
|
234
|
+
mode(HOW_CHANGED)
|
235
|
+
@realSize,clen=file.read(8).unpack('VV')
|
236
|
+
@add = @bytes[@realSize..-1] if @bytes.length > @realSize
|
237
|
+
@bytes = @bytes[0, @realSize]
|
238
|
+
for i in 0..clen-1
|
239
|
+
ofs, bts = file.read(8).unpack('VV')
|
240
|
+
@changes[ofs] = file.read(bts)
|
241
|
+
end
|
242
|
+
@c2 = @changes.keys.reverse
|
243
|
+
mode(HOW_ORIGINAL)
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'resedit/app/app'
|
2
|
+
|
3
|
+
module Resedit
|
4
|
+
|
5
|
+
class HexWriter
|
6
|
+
|
7
|
+
attr_accessor :written
|
8
|
+
|
9
|
+
def initialize(addr)
|
10
|
+
@written = 0
|
11
|
+
@charsInLine = 0x10
|
12
|
+
@addr = addr
|
13
|
+
@col = App::get().col
|
14
|
+
@size = 0
|
15
|
+
@line = nil
|
16
|
+
@cline = ''
|
17
|
+
@chr = ''
|
18
|
+
@cchr = ''
|
19
|
+
@pcol = nil
|
20
|
+
@segments = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def setSegments(segments, sfix)
|
24
|
+
@segments = segments.sort.reverse
|
25
|
+
@sfix = sfix
|
26
|
+
end
|
27
|
+
|
28
|
+
def addrFormat()
|
29
|
+
add = ''
|
30
|
+
if @segments
|
31
|
+
seg = (@addr-@sfix) >> 4
|
32
|
+
min = @segments.find{|e| e <= seg}
|
33
|
+
min = 0 if !min
|
34
|
+
add = sprintf(" %04X:%04X", min, @addr - @sfix - (min << 4))
|
35
|
+
end
|
36
|
+
res = sprintf("%08X%s | ", @addr, add)
|
37
|
+
@addr += @charsInLine
|
38
|
+
return res
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def addBytes(bytes, color=nil)
|
43
|
+
bytes.each_byte{|b| addChar(b, color)}
|
44
|
+
end
|
45
|
+
|
46
|
+
def procColored()
|
47
|
+
@line += @col.color(@pcol, @cline)
|
48
|
+
@chr += @col.color(@pcol, @cchr)
|
49
|
+
@cline = ''
|
50
|
+
@cchr = ''
|
51
|
+
end
|
52
|
+
|
53
|
+
def buildLine()
|
54
|
+
procColored() if @pcol
|
55
|
+
puts @line+" | "+@chr
|
56
|
+
@line = nil
|
57
|
+
@chr=''
|
58
|
+
@size = 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def addChar(c, color = nil)
|
62
|
+
c = c.ord
|
63
|
+
@line = addrFormat if !@line
|
64
|
+
procColored if color != @pcol && @pcol
|
65
|
+
if !color
|
66
|
+
@line += sprintf("%02X ",c)
|
67
|
+
@chr += (c<0x20 || c>0x7E) ? '.' : c.chr
|
68
|
+
else
|
69
|
+
@cline += sprintf("%02X ",c)
|
70
|
+
@cchr += (c<0x20 || c>0x7E) ? '.' : c.chr
|
71
|
+
end
|
72
|
+
@pcol = color
|
73
|
+
@size += 1
|
74
|
+
@written += 1
|
75
|
+
if @size == @charsInLine
|
76
|
+
buildLine()
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def finish()
|
81
|
+
return if @size == 0
|
82
|
+
procColored() if @pcol
|
83
|
+
@line += " " * (@charsInLine - @size)
|
84
|
+
buildLine()
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|