--OHRRPGCE LUMPFIX - Utility to repair damage to RPG format files --(C) Copyright 1997-2005 James Paige and Hamster Republic Productions --Please read LICENSE.txt for GPL License details and disclaimer of liability --See README.txt for code docs include lump.e include spiffy.e include graphics.e include wildcard.e --------------------------------------------------------------------------- constant true=1 constant false=0 constant end_of_file=-1 constant LUMP_NAME=1 constant LUMP_DATA=2 --------------------------------------------------------------------------- sequence prog_dir sequence lump_filename sequence simple_name sequence lump_data integer corrupt corrupt=false sequence lump_list lump_list={{},{}} integer recover_count recover_count=0 --------------------------------------------------------------------------- procedure nl_printf(integer dest,sequence s,sequence printf_args) sequence pos pos=get_position() if pos[2]>1 then printf(dest,"\n",{}) end if printf(dest,s,printf_args) end procedure --------------------------------------------------------------------------- procedure init() sequence args args=command_line() prog_dir=path_only(args[2]) if length(args)>=3 then lump_filename=args[3] simple_name=upper(trim_tail(without_extension(file_only(lump_filename)),'.')) else nl_printf(stdout,"LUMPFIX - (C)2001 James Paige / Hamster Republic Productions\n",{}) printf(stdout,"A utility that attempts to repair corrupted lumpfiles\n",{}) printf(stdout,"usage: LUMPFIX FILENAME.RPG\n",{}) abort(0) end if if not file_exists(lump_filename) then nl_printf(stdout,"Lumpfile %s not found\n",{lump_filename}) abort(1) end if end procedure --------------------------------------------------------------------------- procedure read_file() integer fh integer char nl_printf(stdout,"Reading Lumpfile %s\n",{lump_filename}) lump_data={} fh=open(lump_filename,"rb") if fh=failure then nl_printf(stdout,"file open failed\n",{}) abort(1) else while true do char=getc(fh) if char=end_of_file then exit end if lump_data&=char end while close(fh) end if nl_printf(stdout,"read %d bytes\n",{length(lump_data)}) end procedure --------------------------------------------------------------------------- procedure write_fixed_lumpfile() sequence outfile integer fh outfile=alter_extension(lump_filename,"FIX") if length(lump_list[LUMP_NAME]) then nl_printf(stdout,"%s is corrupt\nwriting fixed file %s\n",{lump_filename,outfile}) fh=open(outfile,"wb") if fh=failure then nl_printf(stdout,"failed to open %s\n",{outfile}) else for i=1 to length(lump_list[LUMP_NAME]) do if not write_lump(fh,lump_list[LUMP_NAME][i],lump_list[LUMP_DATA][i]) then nl_printf(stdout,"failed to write lump %s\n",{lump_list[LUMP_NAME][i]}) end if end for close(fh) nl_printf(stdout,"wrote %d lumps\n",{length(lump_list[LUMP_NAME])}) nl_printf(stdout,"recovered %d lost lumps\n",{recover_count}) nl_printf(stdout,"it is possible that other lumps were unrecoverable\n",{}) end if else nl_printf(stdout,"No valid lumps found.\nIt is unlikely that %s was even a lumpfile in the first place\n",{lump_filename}) end if end procedure --------------------------------------------------------------------------- function get_filename(integer ptr) sequence result integer b result="" for i=ptr to ptr+11 do if i>length(lump_data) then nl_printf(stdout,"file ended in the middle of a lumpname\n",{}) abort(1) end if b=lump_data[i] if b=0 then --found zero-terminator, break the loop exit elsif b<33 or b>126 or find(b,"\"*/\\<>?|") or (b='.' and (find('.',result) or length(result)>8)) then nl_printf(stdout,"found illegal char %d (%s) in filename \"%s\"\n",{b,b,result}) return("") else result&=b end if end for if find(result,lump_list[LUMP_NAME]) then nl_printf(stdout,"detected repeated lump name\n",{}) end if return(result) end function --------------------------------------------------------------------------- function try_get_name(integer ptr) sequence result integer b result="" for i=ptr to ptr+11 do if i>length(lump_data) then nl_printf(stdout,"file ended while seeking a lumpname\n",{}) write_fixed_lumpfile() abort(1) end if b=lump_data[i] if b=0 then --found zero-terminator, break the loop exit elsif b<33 or b>126 or find(b,"\"*/\\<>?|") or (b='.' and (find('.',result) or length(result)>8)) then --found illegal char return("") elsif length(result)>=8 and not find('.',result) and b!='.' then --no . found return("") else result&=b end if end for if find(result,lump_list[LUMP_NAME]) then --detected repeated lump name return("") end if --check for valid RPG file lumps if wildcard_match(simple_name&".*",upper(result)) or compare("ARCHINYM.LMP",upper(result))=0 or compare("BROWSE.TXT",upper(result))=0 or compare("PLOTSCR.LST",upper(result))=0 or compare("DEFPASS.BIN",upper(result))=0 then return(result) end if return("") end function --------------------------------------------------------------------------- function quadbyte_to_dword(sequence data) integer result result=data[3]+data[4]*#100+data[1]*#10000+data[2]*#1000000 return(result) end function --------------------------------------------------------------------------- function get_size(integer ptr) sequence quad integer size if ptr+3>length(lump_data) then nl_printf(stdout,"file ends in size entry\n",{}) write_fixed_lumpfile() abort(1) end if quad=lump_data[ptr..ptr+3] size=quadbyte_to_dword(quad) if size>length(lump_data)-(ptr+3) then nl_printf(stdout,"not enough room left in file for %d byte lump\n",{size}) return(-1) end if return(size) end function --------------------------------------------------------------------------- function seek_possible_name(integer ptr) sequence name integer progress progress=0 corrupt=true nl_printf(stdout,"Lump corruption detected. Attempting to re-track data",{}) while ptr<=length(lump_data) do name=try_get_name(ptr) if length(name) then nl_printf(stdout,"found possible filename candidate %s\n",{name}) return(ptr) else ptr+=1 end if progress+=1 if progress>=1000 then progress=0 puts(stdout,".") end if end while return(ptr) end function --------------------------------------------------------------------------- function read_archinym(sequence data) sequence result sequence broken result="" broken=explode(data,"\n") result=upper(trim_tail(broken[1],13)) nl_printf(stdout,"Found Archinym \"%s\"\n",{result}) return(result) end function --------------------------------------------------------------------------- procedure try_unlump(integer ptr) sequence name integer size while ptr<=length(lump_data) do name=get_filename(ptr) if length(name) then ptr+=length(name)+1 nl_printf(stdout,"lump %-12s",{name}) size=get_size(ptr) if size>=0 then printf(stdout," size %d\n",{size}) ptr+=4 --get lump data lump_list[LUMP_NAME]=append(lump_list[LUMP_NAME],name) lump_list[LUMP_DATA]=append(lump_list[LUMP_DATA],lump_data[ptr..ptr+size-1]) if compare(upper(name),"ARCHINYM.LMP")=0 then simple_name=read_archinym(lump_list[LUMP_DATA][length(lump_list[LUMP_DATA])]) end if if corrupt then recover_count+=1 end if ptr+=size else nl_printf(stdout,"invalid filesize %d\n",{size}) ptr=seek_possible_name(ptr) end if else nl_printf(stdout,"failed to get filename\n",{}) ptr=seek_possible_name(ptr) end if end while if corrupt then write_fixed_lumpfile() else nl_printf(stdout,"no file corruption was detected\n",{}) end if end procedure --------------------------------------------------------------------------- init() read_file() try_unlump(1)