resedit 1.7.3 → 1.8

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.
@@ -4,9 +4,9 @@ module Resedit
4
4
 
5
5
  class HexWriter
6
6
 
7
- attr_accessor :written
7
+ attr_accessor :written, :addressFormatter, :addr
8
8
 
9
- def initialize(addr)
9
+ def initialize(addr, addressFormatter=nil)
10
10
  @written = 0
11
11
  @charsInLine = 0x10
12
12
  @addr = addr
@@ -17,23 +17,15 @@ module Resedit
17
17
  @chr = ''
18
18
  @cchr = ''
19
19
  @pcol = nil
20
- @segments = nil
21
- end
22
-
23
- def setSegments(segments, sfix)
24
- @segments = segments.sort.reverse
25
- @sfix = sfix
20
+ @addressFormatter = addressFormatter
26
21
  end
27
22
 
28
23
  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))
24
+ if @addressFormatter
25
+ res = @addressFormatter.formatAddress(@addr)+" | "
26
+ else
27
+ res = sprintf("%08X | ", @addr)
35
28
  end
36
- res = sprintf("%08X%s | ", @addr, add)
37
29
  @addr += @charsInLine
38
30
  return res
39
31
  end
@@ -0,0 +1,67 @@
1
+ require 'resedit/classes/exefile'
2
+
3
+ module Resedit
4
+
5
+ class BWHeader < ExeHeader
6
+ MAGIC = 0x5742
7
+ HSIZE = 0xB0
8
+ HDRDESCR = [:Magic, :LastPageBytes, :BlocksInFile, :Reserved1, :Reserved2, :MinAlloc, :MaxAlloc, :SS, :SP, :FirstRelocSel, :IP, :CS,
9
+ :RuntimeGdtSize, :MAKEPMVer, :NextHeaderPos, :CVInfoOffset, :LastSelUsed, :PMemAlloc, :AllocIncr, :Reserved4, :Options,
10
+ :TransStackSel, :ExpFlags, :ProgramSize, :GdtImageSize, :FirstSelector, :DefaultMemStrategy, :Reserved5, :TransferBufferSize,
11
+ :Reserved6, :ExpPath]
12
+ HDRUNPACK = "v14V2v3s6v6HHvs48a64"
13
+
14
+ # unsigned_16 signature; /* BW signature to mark valid file */
15
+ # unsigned_16 last_page_bytes; /* length of image mod 512 */
16
+ # unsigned_16 pages_in_file; /* number of 512 byte pages */
17
+ # unsigned_16 reserved1;
18
+ # unsigned_16 reserved2;
19
+ # unsigned_16 min_alloc; /* required memory, in KB */
20
+ # unsigned_16 max_alloc; /* max KB (private allocation) */
21
+ # unsigned_16 stack_seg; /* segment of stack */
22
+ # unsigned_16 stack_ptr; /* initial SP value */
23
+ # unsigned_16 first_reloc_sel; /* huge reloc list selector */
24
+ # unsigned_16 init_ip; /* initial IP value */
25
+ # unsigned_16 code_seg; /* segment of code */
26
+ # unsigned_16 runtime_gdt_size; /* runtime GDT size in bytes */
27
+ # unsigned_16 MAKEPM_version; /* ver * 100, GLU = (ver+10)*100 */
28
+ # /* end of DOS style EXE header */
29
+ # unsigned_32 next_header_pos; /* file pos of next spliced .EXP */
30
+ # unsigned_32 cv_info_offset; /* offset to start of debug info */
31
+ # unsigned_16 last_sel_used; /* last selector value used */
32
+ # unsigned_16 pmem_alloc; /* private xm amount KB if nonzero */
33
+ # unsigned_16 alloc_incr; /* auto ExtReserve amount, in KB */
34
+ # unsigned_8 reserved4[6];
35
+ # /* the following used to be referenced as gdtimage[0..1] */
36
+ # unsigned_16 options; /* runtime options */
37
+ # unsigned_16 trans_stack_sel; /* sel of transparent stack */
38
+ # unsigned_16 exp_flags; /* see ef_ constants below */
39
+ # unsigned_16 program_size; /* size of program in paras */
40
+ # unsigned_16 gdtimage_size; /* size of gdt in file (bytes) */
41
+ # unsigned_16 first_selector; /* gdt[first_sel] = gdtimage[0], 0 => 0x80 */
42
+ # unsigned_8 default_mem_strategy;
43
+ # unsigned_8 reserved5;
44
+ # unsigned_16 transfer_buffer_size; /* default in bytes, 0 => 8KB */
45
+ # /* the following used to be referenced as gdtimage[2..15] */
46
+ # unsigned_8 reserved6[48];
47
+ # char EXP_path[64]; /* original .EXP file name */
48
+
49
+ def headerSize(); HSIZE end
50
+
51
+ def fileSize(); @info[:BlocksInFile] * BLK + @info[:LastPageBytes] end
52
+ def entry; sprintf("%04X:%04X", @info[:CS], @info[:IP]) end
53
+
54
+
55
+ end
56
+
57
+
58
+ class BWBody < ExeBody
59
+ end
60
+
61
+ class BW < ExeFile
62
+ HDRCLASS = BWHeader
63
+ BODYCLASS = BWBody
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,234 @@
1
+ require 'resedit/classes/exefile'
2
+
3
+ module Resedit
4
+
5
+ class LEHeader < ExeHeader
6
+ MAGIC = [0x454C, 0x584C, 0x434C]
7
+ HSIZE = 0xAC
8
+ HDRDESCR = [:Magic, :BOrd, :WOrd, :FormatLevel, :CpuType, :OsType, :ModuleVersion, :ModuleFlags, :ModulePages, :EIPObj, :EIP, :ESPObj, :ESP,
9
+ :PageSize, :PageShift, :FixupSize, :FixupCsum, :LoaderSize, :LoaderCsum, :ObjectTableOfs, :ObjectsInModule,
10
+ :ObjectPageOfs, :ObjectIterOfs, :ResourceTableOfs, :ResourceTableEntries, :ResidentTableOfs, :EntryTableOfs,
11
+ :ModuleDirectivesOfs, :ModuleDirectives, :FixupPageOfs, :FixupRecordOfs, :ImportTblOfs, :ImportEntries, :ImportProcOfs,
12
+ :PerPageCsum, :DataPagesOfs, :PreloadPages, :NonResTableOfs, :NonResTableLen, :NonResTableCsum, :AutoDSObject,
13
+ :DebugInfoOfs, :DebugInfoLen, :InstancePreload, :InstanceDemand, :Heapsize]
14
+ HDRUNPACK = "vHHVvvV40"
15
+
16
+ # 00h | "L" "X" |B-ORD|W-ORD| FORMAT LEVEL |
17
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
18
+ # 08h | CPU TYPE | OS TYPE | MODULE VERSION |
19
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
20
+ # 10h | MODULE FLAGS | MODULE # OF PAGES |
21
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
22
+ # 18h | EIP OBJECT # | EIP |
23
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
24
+ # 20h | ESP OBJECT # | ESP |
25
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
26
+ # 28h | PAGE SIZE | PAGE OFFSET SHIFT / LE: last page bytes !!! |
27
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
28
+ # 30h | FIXUP SECTION SIZE | FIXUP SECTION CHECKSUM|
29
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
30
+ # 38h | LOADER SECTION SIZE |LOADER SECTION CHECKSUM|
31
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
32
+ # 40h | OBJECT TABLE OFF | # OBJECTS IN MODULE |
33
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
34
+ # 48h | OBJECT PAGE TABLE OFF | OBJECT ITER PAGES OFF |
35
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
36
+ # 50h | RESOURCE TABLE OFFSET |#RESOURCE TABLE ENTRIES|
37
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
38
+ # 58h | RESIDENT NAME TBL OFF | ENTRY TABLE OFFSET |
39
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
40
+ # 60h | MODULE DIRECTIVES OFF | # MODULE DIRECTIVES |
41
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
42
+ # 68h | FIXUP PAGE TABLE OFF |FIXUP RECORD TABLE OFF |
43
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
44
+ # 70h | IMPORT MODULE TBL OFF | # IMPORT MOD ENTRIES |
45
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
46
+ # 78h | IMPORT PROC TBL OFF | PER-PAGE CHECKSUM OFF |
47
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
48
+ # 80h | DATA PAGES OFFSET FROM MZ !!! | #PRELOAD PAGES |
49
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
50
+ # 88h | NON-RES NAME TBL OFF | NON-RES NAME TBL LEN |
51
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
52
+ # 90h | NON-RES NAME TBL CKSM | AUTO DS OBJECT # |
53
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
54
+ # 98h | DEBUG INFO OFF | DEBUG INFO LEN |
55
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
56
+ # A0h | #INSTANCE PRELOAD | #INSTANCE DEMAND |
57
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
58
+ # A8h | HEAPSIZE |
59
+ #
60
+
61
+ # Object Table
62
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
63
+ # 00h | VIRTUAL SIZE | RELOC BASE ADDR |
64
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
65
+ # 08h | OBJECT FLAGS | PAGE TABLE INDEX |
66
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
67
+ # 10h | # PAGE TABLE ENTRIES | RESERVED |
68
+ # +-----+-----+-----+-----+-----+-----+-----+-----+
69
+
70
+ attr_reader :tables
71
+
72
+ def initialize(exe, file, fsize)
73
+ @_tablesOrig = nil
74
+ @sofs = file.tell()
75
+ super(exe, file, fsize)
76
+ end
77
+
78
+ def loadTables(file)
79
+ if @_tablesOrig==nil
80
+ objtblend = @sofs+@info[:ObjectTableOfs]+@info[:ObjectsInModule]*0x18
81
+ #addData(file, objtblend-file.tell())
82
+ objtblend = @sofs+@info[:ObjectPageOfs]+@info[:ModulePages]*8
83
+ addData(file, objtblend-file.tell())
84
+ end
85
+ tbl = {:Objects => [], :Pages => []}
86
+ ofs = 0
87
+ for i in 0..@info[:ObjectsInModule]-1
88
+ descr = getData(@info[:ObjectTableOfs]+i*0x18, 0x18).unpack("V*")
89
+ tbl[:Objects] += [descr]
90
+ @exe.env.set("seg#{i}".to_sym, ofs.to_s)
91
+ ofs += descr[4]*@info[:PageSize]
92
+ end
93
+ for i in 0..@info[:ModulePages]-1
94
+ tbl[:Pages] += getData(@info[:ObjectPageOfs]+i*4, 4).unpack("V")
95
+ end
96
+ if @_tablesOrig==nil
97
+ @tables = @_tablesOrig = tbl
98
+ end
99
+ return tbl
100
+ end
101
+
102
+ def loadTail(file);
103
+ addData(file, headerSize() + @sofs - file.tell())
104
+ end
105
+
106
+ def readRelocs()
107
+ def read(pos,cnt, unp); [getData(@info[:FixupRecordOfs]+pos, cnt).unpack(unp),pos+cnt] end
108
+ ret = {}
109
+ pgs = getData(@info[:FixupPageOfs], 4*(@info[:ModulePages]+1)).unpack("V*")
110
+ for i in 0..@info[:ModulePages]-1
111
+ pos = pgs[i]
112
+ op = @tables[:Pages][i]
113
+ pgofs = ((op>>16) + ((op>>8) & 0xFF) -1) * @info[:PageSize]
114
+ ret[pgofs] = {}
115
+ while pos<pgs[i+1]
116
+ v,pos = read(pos, 5, "CCvC")
117
+ raise "Unknown fixup type #{v[0]} #{v[1]}" if (v[0]!=7 && v[0]!=2) || (v[1] & ~0x10 !=0 )
118
+ trg, pos=read(pos, v[1]==0x10 ? 4 : 2,v[1]==0x10 ? "V" : "v")
119
+ next if v[2]>0x7FFF
120
+ ofs = pgofs+v[2]
121
+ ret[pgofs][pgofs+v[2]] = @tables[:Objects][v[3]-1][1]+trg[0]
122
+ end
123
+ end
124
+ return ret
125
+ end
126
+
127
+
128
+ def mode(how)
129
+ super(how)
130
+ if @mode == HOW_ORIGINAL
131
+ @tables = @_tablesOrig
132
+ else
133
+ @_tables = loadTables(nil) if !@_tables
134
+ @tables = @_tables
135
+ end
136
+ end
137
+
138
+ def headerSize(); @info[:DataPagesOfs]-@exe.mzSize end
139
+
140
+ def fileSize(); headerSize()+(@info[:ModulePages]-1)*@info[:PageSize]+@info[:PageShift] end
141
+ def entry; sprintf("0x%08X", @info[:EIP]) end
142
+
143
+ def print(what, how=nil)
144
+ ret = super(what, how)
145
+ if what=="tables"
146
+ puts "Objects: #{@tables[:Objects].map{|o| o.map{|v| v.to_s(16)}}}"
147
+ puts "Pages: #{@tables[:Pages].map{|x| x.to_s(16)}}"
148
+ return true
149
+ end
150
+ return ret
151
+ end
152
+ end
153
+
154
+ class LEBody < ExeBody
155
+
156
+ def initialize(exe, file, fsize)
157
+ super(exe, file, fsize)
158
+ end
159
+
160
+ def sections()
161
+ if !@sex
162
+ ofs = 0
163
+ @sex=[]
164
+ @exe.header.tables[:Objects].each{|descr|
165
+ sz = descr[4] * @exe.header.info[:PageSize]
166
+ if sz!=0
167
+ @sex += [[descr[1], ofs, sz]]
168
+ ofs += sz
169
+ end
170
+ }
171
+ end
172
+ @sex
173
+ end
174
+
175
+ def relocations()
176
+ if !@relocs
177
+ @relocs = @exe.header.readRelocs().sort_by{|k,v| k}.reverse.to_h
178
+ @relocs.each{|k,v|
179
+ @relocs[k] = v.sort_by{|k2,v2| k2}.reverse.to_h
180
+ }
181
+ end
182
+ @relocs
183
+ end
184
+
185
+ def formatAddress(raw)
186
+ return sprintf("%08X %08X", raw, raw2addr(raw))
187
+ end
188
+
189
+ def raw2addr(ofs)
190
+ s = sections.find{|s| s[1]<=ofs && s[1]+s[2]>ofs}
191
+ raise "Not raw offset #{ofs.to_s(16)}" if !s
192
+ return s[0]+ofs-s[1]
193
+ end
194
+
195
+ def addr2raw(addr)
196
+ s = sections.find{|s| s[0]<=addr && s[0]+s[2]>addr}
197
+ raise "Not virtual address #{addr.to_s(16)}" if !s
198
+ return s[1]+addr-s[0]
199
+ end
200
+
201
+ def readRelocated(ofs, size);
202
+ rel = relocations()
203
+ d = getData(ofs, size)
204
+ @relocs.each{|o,v|
205
+ next if o>ofs+size
206
+ v.each{|a, r|
207
+ next if a>ofs+size
208
+ break if a<ofs
209
+ pos = a-ofs
210
+ d = d[0,pos] + [r].pack("V") + (d[pos+4..-1] or '')
211
+ }
212
+ break if o<ofs
213
+ }
214
+ return d[0,size]
215
+ end
216
+
217
+ end
218
+
219
+
220
+ class LE < ExeFile
221
+ HDRCLASS = LEHeader
222
+ BODYCLASS = LEBody
223
+ MODE = 32
224
+
225
+ attr_reader :mzSize
226
+
227
+ def load(file, sz, prev)
228
+ @mzSize = prev.header.fileSize()
229
+ super(file, sz, prev)
230
+ end
231
+
232
+ end
233
+
234
+ end
@@ -0,0 +1,85 @@
1
+ require 'resedit/classes/exefile'
2
+ require 'resedit/mz/mz'
3
+ require 'resedit/mz/bw'
4
+ require 'resedit/mz/le'
5
+
6
+ module Resedit
7
+
8
+ class Multiexe < ExeFile
9
+
10
+ KNOWN_TYPES = {'MZ' => MZ, 'BW' => BW, 'LE' => LE, 'LX' => LE, 'LC'=>LE}
11
+
12
+ attr_reader :cur
13
+
14
+ def initialize(path, quiet = false)
15
+ @cur = nil
16
+ @parts = []
17
+ super(path, quiet)
18
+ end
19
+
20
+ def load(f, fsize)
21
+ cid = 0
22
+ while !f.eof?
23
+ s = f.read(2).unpack('A2')[0]
24
+ f.seek(-2, :CUR)
25
+ #log("Loading part #{s} @ 0x#{f.tell.to_s(16)}")
26
+ raise "Unknown format #{s}" if !KNOWN_TYPES[s]
27
+ obj = KNOWN_TYPES[s].new(nil, @quiet)
28
+ sz = fsize - f.tell()
29
+ obj.load(f, sz, @parts.length>0 ? @parts[-1] : 0 )
30
+ cid = @parts.length() if obj.is_a?(LE)
31
+ @parts += [obj]
32
+ end
33
+ setPart(cid)
34
+ end
35
+
36
+ def loadConfig(cfg)
37
+ @parts.each.with_index{|pr, i|
38
+ pr.loadConfig(cfg[i.to_s])
39
+ }
40
+ end
41
+
42
+ def header; @cur.header end
43
+ def body; @cur.body end
44
+ def env; @cur.env end
45
+
46
+ def setPart(id); @cur = @parts[id] end
47
+ def close(); @parts.each{|pr| pr.close()} end
48
+
49
+ def print(what, how=nil)
50
+ if what=="parts"
51
+ puts "#{@parts.length} parts:"
52
+ @parts.each.with_index{|pr, i|
53
+ puts "#{i}: #{pr.class} #{pr}"
54
+ }
55
+ return true
56
+ end
57
+ @cur.print(what, how)
58
+ end
59
+ def hex(ofs, size=nil, how=nil, disp=nil); @cur.hex(ofs, size, how, disp) end
60
+ def hexify(str); @cur.hexify(str) end
61
+ def getValue(value, type); @cur.getValue(value, type) end
62
+ def append(value, type=nil, where=nil); @cur.append(value, type, where) end
63
+ def replace(value, type=nil, where=nil); @cur.replace(value, type, where) end
64
+ def change(ofs, value, disp=nil, type=nil); @cur.change(ofs, value, disp, type) end
65
+ def reloc(ofs); @cur.reloc(ofs) end
66
+ def dasm(ofs, size=nil, how=nil) @cur.dasm(ofs, size, how) end
67
+ def valueof(str, type); @cur.valueof(str, type) end
68
+ def revert(what); @cur.revert(what) end
69
+ def readRelocated(ofs, size); @cur.readRelocated(ofs, size) end
70
+
71
+ def saveConfig()
72
+ cfg = {}
73
+ @parts.each.with_index{|pr, i|
74
+ cfg[i] = pr.saveConfig()
75
+ }
76
+ return cfg
77
+ end
78
+
79
+ def saveFile(f)
80
+ @parts.each{|pr| pr.saveFile(f)}
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -1,175 +1,282 @@
1
- require 'resedit/mz/mz_header'
2
- require 'resedit/mz/mz_body'
3
- require 'resedit/classes/hexwriter'
4
- require 'resedit/mz/mzenv'
1
+ require 'resedit/classes/exefile'
5
2
 
6
3
  module Resedit
7
4
 
8
- class MZ
9
- ZM = 0x4D5A
10
-
11
- attr_reader :fname, :path, :name, :fsize
12
- attr_reader :header, :body
13
-
14
- def initialize(path, quiet = false)
15
- raise "File not specified" if !path
16
- @quiet = quiet
17
- @path = path.downcase()
18
- @fsize = File.size(path)
19
- open(@path,"rb:ascii-8bit"){|f|
20
- @header = MZHeader.new(self, f, fsize)
21
- hsz = @header.headerSize()
22
- @body = MZBody.new(self, f, @header.fileSize() - hsz)
23
- save = f.read(2)
24
- zm = save ? save.unpack('v')[0] : nil
25
- if zm == ZM
26
- @header.loadChanges(f)
27
- @body.loadChanges(f)
28
- log("Change info loaded.")
29
- end
30
- }
31
- @fname = File.basename(@path)
32
- @name = File.basename(@path, ".*")
33
- hi = @header.info()
34
- env().set(:entry, hi[:CS].to_s+":"+hi[:IP].to_s)
35
- env().set(:append, sprintf("0:0"))
36
- end
5
+ class MZHeader < ExeHeader
6
+ MAGIC = 0x5A4D
7
+ HSIZE = 0x1C
8
+ HDRDESCR = [:Magic, :BytesInLastBlock, :BlocksInFile, :NumberOfRelocations, :HeaderParagraphs, :MinExtraParagraphs, :MaxExtraParagraphs,
9
+ :SS, :SP, :Checksum, :IP, :CS, :RelocTableOffset, :OverlayNumber]
37
10
 
11
+ attr_reader :relocFix
38
12
 
39
- def close()
13
+ def initialize(exe, file, size)
14
+ super(exe, file, size)
15
+ @relocFix = 0
16
+ @newOfs = false
17
+ if @info[:RelocTableOffset]>=0x40
18
+ @newOfs = getData(0x3C, 2).unpack("v")[0]
19
+ end
40
20
  end
41
21
 
22
+ def entry; sprintf("%04X:%04X", @info[:CS], @info[:IP]) end
42
23
 
43
- def log(fmt, *args)
44
- App::get().log(fmt, *args) if !@quiet
24
+ def setFileSize(size)
25
+ mode(HOW_CHANGED)
26
+ mod = size % BLK
27
+ setInfo(:BytesInLastBlock, [mod, size / BLK + (mod ? 1 : 0)])
45
28
  end
46
29
 
30
+ def rebuildHeader(codesize)
31
+ mode(HOW_ORIGINAL)
32
+ ss = @info[:SS]
33
+ cs = @info[:CS]
34
+ sz = fileSize()-headerSize()
35
+ codesize += PARA - codesize % PARA if codesize % PARA!=0
36
+ changeSize(sz + codesize + headerSize())
37
+ paras = codesize / PARA
38
+ setInfo(:SS, ss+paras)
39
+ setInfo(:CS, cs+paras)
40
+ for i in 0..@info[:NumberOfRelocations]-1
41
+ rel = getRelocation(i)
42
+ rel[1] += paras-@relocFix
43
+ fix(@info[:RelocTableOffset] + i * 4, rel.pack('vv'))
44
+ end
45
+ mode(HOW_CHANGED)
46
+ @relocFix = paras
47
+ MZEnv.instance().set(:relocFix, paras.to_s)
48
+ return codesize
49
+ end
47
50
 
48
- def env() return MZEnv.instance() end
49
- def s2i(str) return MZEnv.instance().s2i(str) end
50
-
51
+ def addHeaderSize(size)
52
+ mode(HOW_CHANGED)
53
+ paras = size/16 + (size%16 == 0 ? 0 : 1)
54
+ insert(headerSize(), "\x00" * (paras * PARA))
55
+ setFileSize(fileSize() + paras * PARA)
56
+ setInfo(:HeaderParagraphs, @info[:HeaderParagraphs] + paras)
57
+ mode(HOW_CHANGED)
58
+ end
51
59
 
52
- def is?(id)
53
- id = id.downcase
54
- return id == @path || id == @fname || id == @name
60
+ def loadChanges(cfg)
61
+ super(cfg)
62
+ mode(HOW_ORIGINAL)
63
+ ocs = @info[:CS]
64
+ mode(HOW_CHANGED)
65
+ ncs = @info[:CS]
66
+ @relocFix = ncs - ocs
55
67
  end
56
68
 
69
+ def headerSize(); @info[:HeaderParagraphs] * PARA end
57
70
 
58
- def print(what, how=nil)
59
- puts "Header changes:" if what=="changes"
60
- res = @header.print(what, how)
61
- puts "Code changes:" if what=="changes"
62
- res |= @body.print(what, how)
63
- raise "Don't know how to print: " + what if !res
71
+ def fileSize()
72
+ return @newOfs if @newOfs
73
+ sz = @info[:BlocksInFile] * BLK
74
+ if @info[:BytesInLastBlock] != 0
75
+ sz -= BLK - @info[:BytesInLastBlock]
76
+ end
77
+ return sz
64
78
  end
65
79
 
66
80
 
67
- def hex(ofs, size=nil, how=nil, disp=nil)
68
- ofs = ofs ? s2i(ofs) : 0
69
- size = size ? s2i(size) : 0x100
70
- isfile = disp && (disp[0]=='f' || disp[0]=='F') ? true : false
71
- wr = HexWriter.new(ofs)
72
- how = @header.parseHow(how)
73
- hsz = 0
74
- if isfile
75
- @header.mode(how)
76
- hsz = @header.headerSize()
77
- size = @header.hex(wr, ofs, size, how) if ofs < hsz
78
- ofs -= hsz
79
- ofs = 0 if ofs < 0
80
- end
81
- wr.setSegments(@body.segments, hsz)
82
- @body.hex(wr, ofs, size, how) if size > 0
83
- wr.finish()
81
+ def getRelocation(idx)
82
+ raise "Wrong relocation index " if idx<0 || idx >= @info[:NumberOfRelocations]
83
+ return getData(@info[:RelocTableOffset] + idx * 4, 4).unpack('vv')
84
84
  end
85
85
 
86
+ def setRelocation(idx, data)
87
+ raise "Wrong relocation index " if idx<0 || idx >= @info[:NumberOfRelocations]
88
+ change(@info[:RelocTableOffset] + idx * 4, data.pack('vv'))
89
+ end
90
+
91
+
92
+ def freeSpace(middle = false)
93
+ return @info[:RelocTableOffset] - HSIZE if middle
94
+ return headerSize() - HSIZE - @info[:NumberOfRelocations] * 4
95
+ end
86
96
 
87
- def getValue(value, type)
88
- s = env().value2bytes(value, type)
89
- return s.force_encoding(Encoding::ASCII_8BIT)
97
+ def setSpaceForRelocs(count)
98
+ add = count - @info[:NumberOfRelocations]
99
+ return if add<=0
100
+ add -= freeSpace()/4
101
+ return if add<=0
102
+ addHeaderSize(add*4)
103
+ setInfo(:NumberOfRelocations, count)
104
+ mode(HOW_CHANGED)
90
105
  end
91
106
 
107
+ def addReloc(ofs)
108
+ mode(HOW_CHANGED)
109
+ #check relocation exists
110
+ for i in 0..@info[:NumberOfRelocations]-1
111
+ rel = getRelocation(i)
112
+ if @exe.body.seg2Linear(rel[0], rel[1]) == ofs
113
+ return false
114
+ end
115
+ end
116
+ #add relocation
117
+ setSpaceForRelocs(@info[:NumberOfRelocations]+1)
118
+ val = @exe.body.linear2seg(ofs)
119
+ setRelocation(@info[:NumberOfRelocations], val)
120
+ return true
121
+ end
92
122
 
93
- def append(value, type=nil, where=nil)
94
- where = s2i(where) if where
95
- res = @body.append(getValue(value,type), where)
96
- s = ""
97
- res.each{|a|
98
- if a.is_a?(Array)
99
- s += sprintf(" %04X:%04X", a[1], a[0])
100
- else
101
- s += sprintf(" %08X", a)
123
+ def print(what, how)
124
+ mode(parseHow(how))
125
+ if what == "header"
126
+ ofs=0
127
+ @info.each{|k,v|
128
+ printf("%20s:\t%s\n", k.to_s, colVal(ofs, 2))
129
+ ofs+=2
130
+ }
131
+ puts
132
+ fsz = fileSize()
133
+ hsz = headerSize()
134
+ s = colStr(sprintf("%d (%X)", fsz,fsz), changed?(2, 4))
135
+ printf("mz file size: %s\treal file size: %d (0x%X)\n", s, @fsize, @fsize)
136
+ printf("header size: %s\n", colStr(hsz, changed?(8, 2)))
137
+ printf("code size: %s\n", colStr(fsz - hsz, @mz.body.changed?(0)))
138
+ printf("reloc table size: %s\n", colStr(@info[:NumberOfRelocations] * 4, changed?(6, 2)))
139
+ printf("free space in header: before relocs 0x%X, after relocs 0x%X\n", freeSpace(true), freeSpace())
140
+ printf("reloc fix: 0x%X\n", @relocFix)
141
+ return true
142
+ end
143
+ @exe.body.mode(@mode)
144
+ if what == "reloc"
145
+ ofs = @info[:RelocTableOffset]
146
+ for i in 0..@info[:NumberOfRelocations]-1
147
+ s1 = colVal(ofs,2)
148
+ s2 = colVal(ofs+2,2)
149
+ s3 = @exe.body.segData(getRelocation(i), 2, true)
150
+ printf("%08X\t%s:%s\t= %s\n", ofs, s2, s1, s3)
151
+ ofs += 4
102
152
  end
103
- }
104
- log("Appended at %s",s)
105
- return res
153
+ return true
154
+ end
155
+ return super(what, how)
106
156
  end
107
157
 
158
+ end
159
+
108
160
 
109
- def replace(value, type=nil, where=nil)
110
- @body.removeAppend()
111
- return append(value,type, where)
161
+ class MZBody < ExeBody
162
+
163
+ attr_reader :segments
164
+
165
+ def initialize(exe, file, size)
166
+ super(exe, file, size)
167
+ @segments = nil
112
168
  end
113
169
 
170
+ def reloadSegments()
171
+ @segments = Set.new()
172
+ for i in 0..@exe.header.info[:NumberOfRelocations]-1
173
+ r = @exe.header.getRelocation(i)
174
+ @segments.add(r[1])
175
+ sd = segData(r, 2)
176
+ next if !sd
177
+ val = sd.unpack('v')[0]
178
+ @segments.add(val)
179
+ end
180
+ @msegs = @segments.sort.reverse
181
+ end
114
182
 
115
- def change(ofs, value, disp=nil, type=nil)
116
- ofs = s2i(ofs)
117
- isfile = disp && (disp[0]=='f' || disp[0]=='F') ? true : false
118
- value = getValue(value, type)
119
- if isfile
120
- res = @header.change(ofs,value)
121
- else
122
- res = @body.change(ofs,value) + @header.headerSize()
183
+ def patchRelocs(add)
184
+ @segments = Set.new()
185
+ for i in 0..@exe.header.info[:NumberOfRelocations]-1
186
+ r = @exe.header.getRelocation(i)
187
+ @segments.add(r[1])
188
+ ofs = seg2Linear(r[0], r[1])
189
+ val = getData(ofs, 2).unpack('v')[0] + add
190
+ fix(ofs, [val].pack('v'))
191
+ @segments.add(val)
123
192
  end
124
- log("Change added at %08X", res) if res
193
+ @msegs = @segments.sort.reverse
125
194
  end
126
195
 
127
- def reloc(ofs)
128
- ofs = s2i(ofs)
129
- res = @header.addReloc(ofs)
130
- log((res ? "Relocation added %08X" : "Relocation %08X already exists"), ofs)
196
+ def seg2Linear(a,s) (s << 4) + a end
197
+
198
+ def seg4Linear(linear)
199
+ linear >>= 4
200
+ reloadSegments() if !@segments
201
+ min = @segments.sort.reverse.find{|e| e <= linear}
202
+ return min ? min : 0
131
203
  end
132
204
 
133
205
 
134
- def dasm(ofs, size=nil, how=nil)
135
- ofs = s2i(ofs ? ofs : "entry")
136
- size = size ? s2i(size) : [0x20, @body.bytes.length-ofs].min
137
- @body.dasm(ofs, size, how)
206
+ def linear2seg(linear, inSegments=nil)
207
+ inSegments = [seg4Linear(linear)] if !inSegments
208
+ res = []
209
+ inSegments.each{|s|
210
+ raise sprintf("Linear %X less than segment %04X", inSegments[0], s) if linear < (s<<4)
211
+ a = linear - (s << 4)
212
+ res += [a,s]
213
+ }
214
+ return res
138
215
  end
139
216
 
140
217
 
141
- def valueof(str, type)
142
- puts "value of " + str + " is:"
143
- p getValue(str, type).unpack("H*")
218
+ def segData(reloc, size, isStr=false)
219
+ ofs = seg2Linear(reloc[0], reloc[1])
220
+ return nil if ofs > @root.size()
221
+ return getData(ofs, size) if !isStr
222
+ return colVal(ofs, size)
144
223
  end
145
224
 
225
+ def formatAddress(addr)
226
+ @sfix=0
227
+ seg = (addr-@sfix) >> 4
228
+ min = @msegs.find{|e| e <= seg}
229
+ min = 0 if !min
230
+ return sprintf("%08X %04X:%04X", addr, min, addr - @sfix - (min << 4))
231
+ end
146
232
 
147
- def revert(what)
148
- wid = env().s2i_nt(what)
149
- what = wid[1] ? wid[0] : what
150
- res = @header.revert(what)
151
- res |= @body.revert(what)
152
- raise "Don't know how to revert: "+what if !res
153
- log("Reverted")
233
+ def raw2addr(ofs); linear2seg(ofs) end
234
+ def addr2raw(addr); seg2Linear(addr[0], addr[1]) end
235
+
236
+
237
+ def append(bytes, where=nil)
238
+ mode(HOW_CHANGED)
239
+ relfix = @exe.header.relocFix
240
+ res = @addsz
241
+ buf = @addsz>0 ? @root.nbuf[0, @addsz] : ''
242
+ buf += bytes
243
+ removeAppend()
244
+ @addsz = buf.length
245
+ sz = @exe.header.rebuildHeader(@addsz)
246
+ insert(0, bytes + "\x00"*(sz-@addsz))
247
+ seg = linear2seg(res)
248
+ res = [res, seg, sz/0x10]
249
+ patchRelocs(sz/0x10 - relfix)
250
+ return res
154
251
  end
155
252
 
156
- def hexify(str)
157
- str.each_byte.map { |b| sprintf("%02X",b) }.join
253
+
254
+ def print(what, how)
255
+ if what=="header"
256
+ reloadSegments() if !@segments
257
+ puts "Known segments: " + @segments.sort.map{ |i| sprintf('%04X',i) }.join(", ")
258
+ return true
259
+ end
260
+ return super(what, how)
158
261
  end
159
262
 
160
263
 
161
- def save(filename, final=nil)
162
- raise "Unknown final: " + final if final && final != "final"
163
- raise "Filename expected." if !filename
164
- open(filename, "wb:ascii-8bit"){|f|
165
- @header.saveData(f)
166
- @body.saveData(f)
167
- if !final || final!='final'
168
- f.write([ZM].pack('v'))
169
- @header.saveChanges(f)
170
- @body.saveChanges(f)
171
- end
172
- }
264
+ def printDasm(inst, str)
265
+ seg = linear2seg(inst.address)
266
+ printf("%08X %04X:%04X%s\n",inst.address, seg[1], seg[0], str)
267
+ end
268
+
269
+ end
270
+
271
+
272
+ class MZ < ExeFile
273
+ HDRCLASS = MZHeader
274
+ BODYCLASS = MZBody
275
+
276
+ def reloc(ofs)
277
+ ofs = s2i(ofs)
278
+ res = @header.addReloc(ofs)
279
+ log((res ? "Relocation added %08X" : "Relocation %08X already exists"), ofs)
173
280
  end
174
281
 
175
282
  end