resedit 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,192 @@
|
|
1
|
+
require 'resedit/app/app'
|
2
|
+
require 'resedit/app/app_command'
|
3
|
+
require 'resedit/mz/mz'
|
4
|
+
require 'resedit/mz/mzenv'
|
5
|
+
|
6
|
+
|
7
|
+
module Resedit
|
8
|
+
|
9
|
+
class MZCommand < AppCommand
|
10
|
+
def initialize
|
11
|
+
super(['mz'])
|
12
|
+
addParam('cmd', "mz command","")
|
13
|
+
addParam('p1', "mz command parameter","")
|
14
|
+
addParam('p2', "mz command parameter","")
|
15
|
+
addParam('p3', "mz command parameter","")
|
16
|
+
addParam('p4', "mz command parameter","")
|
17
|
+
addParam('p5', "mz command parameter","")
|
18
|
+
addOption('help', 'h', nil, 'help on mz commands')
|
19
|
+
@cmds = {
|
20
|
+
"help"=>[method(:help), "show help on mz commands", {"command" => "command to show help on"}],
|
21
|
+
"use"=>[method(:use), "select mz file", {"file" => "path to mz file"}],
|
22
|
+
"save"=>[method(:save), "save current file",{"filename" => "filename fir saving", "final"=>"don't save changes"}],
|
23
|
+
"close"=>[method(:close), "close file", {"file" => "path or id of file to close"}],
|
24
|
+
"print"=>[method(:info), "print info about mz objects", {"what" => "files/header/reloc/changes", "how" => "original/modified"}],
|
25
|
+
"append"=>[method(:append), "add bytes to current file", {"value" => "value", "type" => "value type", }],
|
26
|
+
"replace"=>[method(:replace), "replace added bytes", {"value" => "value", "type"=>"value type"}],
|
27
|
+
"change"=>[method(:change), "change bytes at offset", {"ofs" => "data ofset", "value" => "value", "disp" => "code/file", "type"=>"value type"}],
|
28
|
+
"revert"=>[method(:revert), "revert changes", {"ofs"=>"change offset/all"}],
|
29
|
+
"hex"=>[method(:hex), "print hex file", {"ofs" => "data offset", "size" => "data size", "how"=>"original/modified", "disp" => "code/file"}],
|
30
|
+
"dasm"=>[method(:dasm), "print disasm", {"ofs" => "data offset", "size" => "data size", "how"=>"original/modified"}],
|
31
|
+
}
|
32
|
+
@files = []
|
33
|
+
@cur = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def help(params)
|
38
|
+
if params['command']
|
39
|
+
raise "Unknown mz command: " + params['command'] if !@cmds[params['command']]
|
40
|
+
cmd = @cmds[params['command']]
|
41
|
+
puts(params['command'] + "\t-" + cmd[1])
|
42
|
+
if cmd[2]
|
43
|
+
puts
|
44
|
+
puts("params:")
|
45
|
+
cmd[2].each{|k,v|
|
46
|
+
puts k + "\t-" + v
|
47
|
+
}
|
48
|
+
end
|
49
|
+
else
|
50
|
+
puts("available mz commands:")
|
51
|
+
@cmds.each{|k,v|
|
52
|
+
puts k + "\t-" + v[1]
|
53
|
+
}
|
54
|
+
end
|
55
|
+
puts
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def getfile(id)
|
60
|
+
return @cur if id == nil
|
61
|
+
i,res=MZEnv.instance.s2i_nt(id)
|
62
|
+
if res
|
63
|
+
raise "Bad file id: " + i.to_s if @files.length < i || i < 0
|
64
|
+
return @files[i]
|
65
|
+
end
|
66
|
+
@files.each{|mz|
|
67
|
+
return mz if mz.is?(id)
|
68
|
+
}
|
69
|
+
return nil
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def info(params=nil)
|
74
|
+
what = params['what'] if params
|
75
|
+
if what == nil || what == "files"
|
76
|
+
if @files.length != 0
|
77
|
+
curid = -1
|
78
|
+
puts "Opened files:"
|
79
|
+
@files.each.with_index{|mz,i|
|
80
|
+
puts "#{i}:\t#{mz.path}"
|
81
|
+
curid=i if mz == @cur
|
82
|
+
}
|
83
|
+
puts "Current file: (#{curid}) #{@cur.path}"
|
84
|
+
puts
|
85
|
+
else
|
86
|
+
puts "No files opened"
|
87
|
+
end
|
88
|
+
else
|
89
|
+
raise "MZ file not loaded" if !@cur
|
90
|
+
@cur.print(what, params['how'])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
def use(params)
|
96
|
+
App::get().col.on = true
|
97
|
+
mz = getfile(params['file'])
|
98
|
+
if mz==nil
|
99
|
+
mz = MZ.new(params['file'])
|
100
|
+
@files+=[mz]
|
101
|
+
end
|
102
|
+
@cur = mz
|
103
|
+
info()
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
def close(params)
|
108
|
+
mz = getfile(params['file'])
|
109
|
+
raise "File not found: "+fn if nil == fl
|
110
|
+
@files -= [mz]
|
111
|
+
@cur = nil if @cur == mz
|
112
|
+
mz.close()
|
113
|
+
mz = nil
|
114
|
+
@cur = @files[0] if !@cur && @files.length > 0
|
115
|
+
App::get().col.on = false if @files.length == 0
|
116
|
+
info()
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
def cur()
|
121
|
+
raise "No MZ selected." if !@cur
|
122
|
+
return @cur
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def save(params)
|
127
|
+
cur().save(params['filename'], params['final'])
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
def append(params)
|
132
|
+
cur().append(params['value'], params['type'])
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def replace(params)
|
137
|
+
cur().replace(params['value'], params['type'])
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
def change(params)
|
142
|
+
cur().change(params['ofs'], params['value'], params['disp'], params['type'])
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
def revert(params)
|
147
|
+
cur().revert(params['ofs'])
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def hex(params)
|
152
|
+
cur().hex(params['ofs'], params['size'], params['how'], params['disp'])
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
def dasm(params)
|
157
|
+
cur().dasm(params['ofs'], params['size'], params['how'])
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
def job(params)
|
162
|
+
cmd = params['cmd']
|
163
|
+
if cmd.length==0 || File.exist?(cmd)
|
164
|
+
App::get().setShell('mz')
|
165
|
+
return if cmd.length == 0
|
166
|
+
params['p1'] = cmd
|
167
|
+
cmd = "use"
|
168
|
+
end
|
169
|
+
if cmd=="valueof"
|
170
|
+
cur().valueof(params['p1'], params['p2'])
|
171
|
+
return
|
172
|
+
end
|
173
|
+
if params['help']
|
174
|
+
params['command'] = params['help']
|
175
|
+
help(params)
|
176
|
+
return
|
177
|
+
end
|
178
|
+
raise "Unknown command: "+cmd if !@cmds[cmd]
|
179
|
+
scmd = @cmds[cmd]
|
180
|
+
if scmd[2]
|
181
|
+
scmd[2].keys.each.with_index{|k,i|
|
182
|
+
params[k] = params["p#{i+1}"]
|
183
|
+
params[k] = nil if params[k].length == 0
|
184
|
+
}
|
185
|
+
end
|
186
|
+
scmd[0].call(params)
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'resedit/app/app_command'
|
2
|
+
require 'resedit'
|
3
|
+
|
4
|
+
module Resedit
|
5
|
+
|
6
|
+
class VersionCommand < AppCommand
|
7
|
+
def initialize
|
8
|
+
super(['version','--version'])
|
9
|
+
end
|
10
|
+
def job(params)
|
11
|
+
log('%s v%s %s. Resedit v%s.',App::get().name, App.get().version, App::get().copyright, Resedit::VERSION)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class ExitCommand < AppCommand
|
16
|
+
def initialize
|
17
|
+
super(['quit','q', 'exit'])
|
18
|
+
end
|
19
|
+
def job(params)
|
20
|
+
App::get().quit();
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
class HelpCommand < AppCommand
|
26
|
+
def initialize
|
27
|
+
super(['help','--help','-h','/?'])
|
28
|
+
addParam('command', 'help on specific command', '')
|
29
|
+
end
|
30
|
+
def job(params)
|
31
|
+
App.get().commands.each{|c|
|
32
|
+
puts c.names[0]
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
class ScriptCommand < AppCommand
|
39
|
+
def initialize
|
40
|
+
super(['script', '--script', '-s'])
|
41
|
+
addParam('file', 'script file')
|
42
|
+
end
|
43
|
+
def job(params)
|
44
|
+
App::get().logd("running script %s", params['file']);
|
45
|
+
script = []
|
46
|
+
text=File.open(params['file']).read
|
47
|
+
text.gsub!(/\r\n?/, "\n")
|
48
|
+
text.each_line {|line|
|
49
|
+
script += [App.get().parseCommand(line.chomp())]
|
50
|
+
}
|
51
|
+
script.each{|cmd|
|
52
|
+
cmd[0].run(cmd[1]) if cmd
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
class ShellCommand < AppCommand
|
59
|
+
def initialize
|
60
|
+
super(['shell'])
|
61
|
+
addParam('shell', 'script file', "")
|
62
|
+
end
|
63
|
+
def job(params)
|
64
|
+
App::get().setShell(params['shell'])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'resedit/app/io_commands'
|
2
|
+
|
3
|
+
module Resedit
|
4
|
+
|
5
|
+
class TextConvertCommand < ConvertCommand
|
6
|
+
|
7
|
+
def initialize(fname)
|
8
|
+
super('text', fname)
|
9
|
+
@font = nil
|
10
|
+
addOption('format','f',nil,'output file format')
|
11
|
+
addOption('encodinc','e',nil,'output file encoding')
|
12
|
+
end
|
13
|
+
|
14
|
+
def import(inname)
|
15
|
+
logd("importing text #{inname} to #{@resname}")
|
16
|
+
back = backup()
|
17
|
+
File.open(back,"rb"){|file|
|
18
|
+
@text = mktext(file, @params['format'], @params['encoding'])
|
19
|
+
@text.load(inname, linesCount())
|
20
|
+
StringIO.open("","w+b"){|stream|
|
21
|
+
pack(file, stream)
|
22
|
+
stream.seek(0)
|
23
|
+
File.open(@resname,"wb"){|out|
|
24
|
+
out.write(stream.read())
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def export(outname)
|
32
|
+
logd("exporting txet #{@resname} to #{outname}")
|
33
|
+
File.open(@resname, "rb"){|file|
|
34
|
+
@text = mktext(file, @params['format'], @params['encoding'])
|
35
|
+
unpack(file) if @text
|
36
|
+
}
|
37
|
+
raise "Text not unpacked" if !@text
|
38
|
+
@text.save(outname)
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def mktext(file, format, encoding)
|
43
|
+
raise "Not implemented."
|
44
|
+
end
|
45
|
+
|
46
|
+
def linesCount(file)
|
47
|
+
raise "Not implemented"
|
48
|
+
end
|
49
|
+
|
50
|
+
def pack(file, outstream)
|
51
|
+
raise "Not implemented."
|
52
|
+
end
|
53
|
+
|
54
|
+
def unpack(file)
|
55
|
+
raise "Not implemented."
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Resedit
|
2
|
+
|
3
|
+
class BitConverter
|
4
|
+
|
5
|
+
def self.bits2Bytes(bits, width)
|
6
|
+
i=0
|
7
|
+
res=[]
|
8
|
+
while i<bits.length
|
9
|
+
buf=0
|
10
|
+
cw=0
|
11
|
+
bsz=0
|
12
|
+
while cw<width
|
13
|
+
if bsz==0
|
14
|
+
buf = bits[i]
|
15
|
+
i+=1
|
16
|
+
bsz=8
|
17
|
+
end
|
18
|
+
bsz-=1
|
19
|
+
cw+=1
|
20
|
+
res << ((buf>>bsz) & 1)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
return res
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.bytes2Bits(bytes, rwidth, bwidth)
|
27
|
+
res=[]
|
28
|
+
for i in 0..(bytes.length/rwidth)-1
|
29
|
+
row=bytes[i*rwidth..i*rwidth+rwidth-1]
|
30
|
+
while row.length < bwidth*8
|
31
|
+
row << 0
|
32
|
+
end
|
33
|
+
b=0
|
34
|
+
for j in 0..row.length-1
|
35
|
+
b <<= 1
|
36
|
+
b |= row[j]
|
37
|
+
if (j+1)%8==0
|
38
|
+
res << (b & 0xFF)
|
39
|
+
b=0
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
return res
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'resedit/font/font_char'
|
2
|
+
require 'resedit/image/image_factory'
|
3
|
+
|
4
|
+
module Resedit
|
5
|
+
|
6
|
+
class Font
|
7
|
+
attr_reader :count, :width, :height
|
8
|
+
attr_accessor :gridColor, :charColor, :userData, :widthColor
|
9
|
+
|
10
|
+
# charWidth, charHeight, characters count
|
11
|
+
def initialize(width, height, count=256)
|
12
|
+
@width, @height, @count = width, height, count
|
13
|
+
@gridColor = 0xFFEEEEEE
|
14
|
+
@charColor = 0xFF000000
|
15
|
+
@bgColor = 0xFFFFFFFF
|
16
|
+
@widthColor = 0xFFFF0000
|
17
|
+
@chars = {}
|
18
|
+
@userData = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def setChar(id, data, width=nil)
|
22
|
+
width=@width if !width
|
23
|
+
@chars[id] = FontChar.new(@width, @height, id, data, width)
|
24
|
+
end
|
25
|
+
|
26
|
+
def getChar(id)
|
27
|
+
@chars[id].data if @chars[id]
|
28
|
+
end
|
29
|
+
|
30
|
+
def minChar
|
31
|
+
return @chars.keys().min
|
32
|
+
end
|
33
|
+
|
34
|
+
def maxChar
|
35
|
+
return @chars.keys().max
|
36
|
+
end
|
37
|
+
|
38
|
+
def charWidth(id)
|
39
|
+
return nil if !@chars[id]
|
40
|
+
return @chars[id].realWidth ? @chars[id].realWidth : @width
|
41
|
+
end
|
42
|
+
|
43
|
+
def save(filename)
|
44
|
+
rows = @count/16 + (@count%16 == 0 ? 0 : 1)
|
45
|
+
img = Resedit.createImage(@width*16+17 , @height*rows+rows+1, filename)
|
46
|
+
img.fill(@bgColor)
|
47
|
+
#draw grid
|
48
|
+
for i in 0..16
|
49
|
+
img.vline(i*(@width+1), gridColor)
|
50
|
+
end
|
51
|
+
for j in 0..rows
|
52
|
+
img.hline(j*(@height+1), gridColor)
|
53
|
+
end
|
54
|
+
#draw letters
|
55
|
+
@chars.each { |idx,c|
|
56
|
+
x = idx%16
|
57
|
+
y = idx/16
|
58
|
+
x += 1+x*@width
|
59
|
+
y += 1+y*@height
|
60
|
+
c.draw(img, @charColor, x, y, @widthColor)
|
61
|
+
}
|
62
|
+
img.save(filename)
|
63
|
+
end
|
64
|
+
|
65
|
+
def load(filename)
|
66
|
+
img = Resedit.loadImage(filename)
|
67
|
+
rows = @count/16 + (@count%16 == 0 ? 0 : 1)
|
68
|
+
raise "Wrong image size" if (img.width!=@width*16+17 || img.height!=@height*rows+rows+1)
|
69
|
+
for idx in 0..@count-1
|
70
|
+
x = idx%16
|
71
|
+
y = idx/16
|
72
|
+
x += 1+x*@width
|
73
|
+
y += 1+y*@height
|
74
|
+
c = FontChar.new(width,height,idx)
|
75
|
+
c.scan(img, @charColor, x, y, @widthColor)
|
76
|
+
@chars[c.index] = c if c.data
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|