RubyでHSPのオブジェクトファイルのインスペクタを作り中:3

行番号の表示とか。そろそろメソッド分割しないとやばい。

TYPENAMES = %w(
TYPE_MARK TYPE_VAR TYPE_STRING TYPE_DNUM TYPE_INUM
TYPE_STRUCT TYPE_XLABEL TYPE_LABEL TYPE_INTCMD TYPE_EXTCMD
TYPE_EXTSYSVAR TYPE_CMPCMD TYPE_MODCMD TYPE_INTFUNC
TYPE_SYSVAR TYPE_PROGCMD TYPE_DLLFUNC TYPE_DLLCTRL
TYPE_USERDEF )

TYPENAMES.each_with_index do |typename, i|
	Object.const_set( typename, i )
end

INTCMDS = %w(
onexit onerror onkey onclick oncmd * * * * * * * * * * * *
exist delete mkdir chdir dirlist bload bsave bcopy memfile
poke wpoke lpoke getstr chdpm memexpand memcpy memset
notesel noteadd notedel noteload notesave randomize
noteunsel noteget )

EXTCMDS = %w(
button chgdisp exec dialog * * * * mmload mmplay mmstop mci
pset pget syscolor mes title pos circle cls font sysfont
objsize picload color palcolor palette redraw width gsel
gcopy gzoom gmode bmpsave hsvcolor getkey listbox chkbox
combox input mesbox buffer screen bgscr mouse objsel groll
line clrobj boxf objprm objmode stick grect grotate gsquare )

EXTSYSVARS = %w( mousex mousey mousew hwnd hinstance hdc )

EXTSYSVARS[0x100,0] = %w( ginfo objinfo dirinfo sysinfo )

CMPCMDS = %w( if else )

INTFUNCS = %w(
int rnd strlen length length2 length3 length4 vartype
gettime peek wpeek lpeek varptr varuse noteinfo instr abs
limit )

INTFUNCS[0x100,0] = %w( str strmid strf getpath )

INTFUNCS[0x180,0] = 
	%w( sin cos tan atan sqrt double absf expf logf limitf )

SYSVARS = %w(
system hspstat hspver stat cnt err strsize looplev sublev
iparam wparam lparam refstr refdval )

PROGCMDS = %w(
goto gosub return break repeat loop continue wait await dim
sdim foreach * dimtype dup dupptr end stop newmod * delmod
mref run exgoto on mcall assert logmes )

DLLCTRLS = %w(
newcom querycom delcom cnvstow comres axobj winobj sendmsg
comevent comevarg sarrayconv )

DLLCTRLS[0x100,0] = %w( callfunc cnvwtos comevdisp libptr )

RESERVEDS = { 
	TYPE_INTCMD => INTCMDS, TYPE_EXTCMD => EXTCMDS, 
	TYPE_EXTSYSVAR => EXTSYSVARS, TYPE_CMPCMD => CMPCMDS, 
	TYPE_INTFUNC => INTFUNCS, TYPE_SYSVAR => SYSVARS, 
	TYPE_PROGCMD => PROGCMDS, TYPE_DLLCTRL => DLLCTRLS }

def get_ds( ds, i )
	ds[i..-1].split("\0")[0]
end

obj = ARGF.read
max_val = obj[8,4].unpack('i')[0]
pt_cs, max_cs, pt_ds, max_ds, pt_ot, max_ot, 
	pt_dinfo, max_dinfo = obj[16,32].unpack('i8')
cs = obj[pt_cs, max_cs]
ds = obj[pt_ds, max_ds]
ot = obj[pt_ot, max_ot]
dinfo = obj[pt_dinfo, max_dinfo]

labels = {}
ot.unpack( 'i*' ).each_with_index do |ofs,id|
	if labels[ofs]
		labels[ofs] << id
	else
		labels[ofs] = [id]
	end
end

varnames = []
lines = []

i = 0
while i < max_dinfo && dinfo[i] != 255
	ofs = dinfo[i]
	i += 1
	case ofs
	when 254
		filename = get_ds( ds, (dinfo[i,3]+"\0").unpack('i')[0] )
		lineno = dinfo[i+3,2].unpack('s')[0]
		lines << [filename, lineno]
		i += 3 + 2
	when 253
		varnames << get_ds( ds, (dinfo[i,3]+"\0").unpack('i')[0] )
		i += 3 + 2
	when 252
		lines << dinfo[i,2].unpack('s')[0]
		i += 2
	else
		lines << ofs
	end
end

filename = nil
lineno = 1
lines_ptr = 0

i = 0
offset = 0
while i < max_cs
	loop do
		if Array === lines[lines_ptr]
			filename, lineno = lines[lines_ptr]
			lines_ptr += 1
			next
		end
		break unless lines[lines_ptr] && offset/2 >= lines[lines_ptr]
		offset = 0#-= lines[lines_ptr]
		lineno += 1
		lines_ptr += 1
	end
	if labels[i/2]
		labels[i/2].each do |id|
			puts "*label#{id}"
		end
	end
	print "#{filename}##{lineno} "
	c = *cs[i,2].unpack('s')
	i += 2
	offset += 2
	type = c & 0x1fff
	ex1 = c & 0x2000 != 0
	ex2 = c & 0x4000 != 0
	if c & 0x8000 != 0
		code = cs[i,4].unpack('i')[0]
		i += 4
		offset += 4
	else
		code = cs[i,2].unpack('S')[0]
		i += 2
		offset += 2
	end
	print "#{TYPENAMES[type]}"
	case type
	when TYPE_MARK
		if (0x20..0x7e) === code
			print "('#{code.chr}')"
		else
			print '(0x%02x)' % code
		end
	when TYPE_VAR
		print '(%p)' % ( varnames[code] || code )
	when TYPE_STRING
		print '(%p)' % get_ds( ds, code )
	when TYPE_DNUM
		print '(%s)' % ds[code,8].unpack('d')
	when TYPE_INUM
		print "(#{code})"
	when *RESERVEDS.keys
		if RESERVEDS[type][code]
			print '(%s)' % RESERVEDS[type][code]
		else
			print '(0x%x)' % code
		end
		if type == TYPE_CMPCMD
			print ' ', cs[i,2].unpack('s')[0]
			i += 2
			offset += 2
		end
	else
		print '(0x%x)' % code
	end
	print ' ex1' if ex1
	print ' ex2' if ex2
	puts
end