/* REXX */

/*
-----------------------------------------------------------------
  unpacker (v0.1) = public domain : free for any use

  AUTHOR: rugxulo _AT_ gmail

  TESTED: Regina 3.7, BRexx 2.1.9, r4 4.00, ooREXX 4.1.3

  BUG:    Can't use literal '*'+'/' pair in embedded data files.
-----------------------------------------------------------------
*/

/* --- UNPACKER BEGINS --- */

if arg() \= 0 then parse arg onlyfile . ; else onlyfile=''
parse source . . srcfile . ; lineno=0 ; writeln=0

bar = '===' ; prefix='/*' bar ; postfix = bar '*/'
headpost=' begins' postfix ; footpost=' ends' postfix
headlen=length(headpost) ; footlen=length(footpost)

if lines(srcfile)=1 then do while lines(srcfile) \= 0
  call grab
end
else do lines(srcfile)
  call grab
end

exit

grab:
  line=linein(srcfile) ; lineno=lineno+1
  if pos(headpost,line) \= 0 then do
    parse var line ' ' (bar) ' ' outfile (headpost) .
    if onlyfile='' then say outfile
    writeln=1
  end
  else if pos(footpost,line) \= 0 then writeln=0
  if pos(headpost,line)=0 & pos(footpost,line)=0 & writeln then ,
    if onlyfile='' | onlyfile=outfile then ,
      call lineout outfile, line
return

/* --- UNPACKER ENDS --- */

/*
------------------------------------------------------------
*** DATA BEGINS DATA BEGINS DATA BEGINS DATA BEGINS ***

/* these data files = public domain : free for any use */
------------------------------------------------------------

/* === bytename.ob2 begins === */
(* public domain, nenies proprajho, free for any use *)
MODULE bytename;
IMPORT S := Str;

CONST maxbytenames=60; maxbytenamelen=19+1;
TYPE bytenamestr=ARRAY maxbytenamelen OF CHAR;
VAR bytenames*:ARRAY maxbytenames OF bytenamestr; bytenum:INTEGER;

PROCEDURE getbytenames*(s:ARRAY OF CHAR);
BEGIN
  IF ((s[0] >= 'A') & (s[0] <= 'Z')) & (S.Pos(" DB ",s,0) >= 0) THEN
    S.Extract(s,0,S.Pos(" ",s,0),bytenames[bytenum]); INC(bytenum)
  END
END getbytenames;

PROCEDURE isbytename*(name:ARRAY OF CHAR):BOOLEAN;
VAR i:INTEGER; done:BOOLEAN;
BEGIN i := 0; done := FALSE;
  WHILE (i < maxbytenames) & ~done DO
    done := bytenames[i] = name;
    INC(i)
  END;
  RETURN done
END isbytename;

BEGIN bytenum := 0
END bytename.
/* === bytename.ob2 ends === */

/* === loadfile.ob2 begins === */
(* public domain, nenies proprajho, free for any use *)
MODULE loadfile;
IMPORT B := bytename,F := Myfiles,S := Str;

CONST maxlines=2276;
TYPE pstring*=POINTER TO ARRAY OF CHAR;
     doitproc*=PROCEDURE (VAR line:pstring);
VAR src:ARRAY maxlines+1 OF pstring; linesread:INTEGER;
    oldasm*,newasm*:F.File;

PROCEDURE load*;
VAR line:ARRAY 131+1 OF CHAR; bytesread:INTEGER;

  PROCEDURE nocomments;
  VAR p:INTEGER;
  BEGIN
    p := S.Pos(';',line,0);
    IF p >= 0 THEN
      IF p > 0 THEN DEC(p) END; WHILE line[p] = ' ' DO DEC(p) END;
      S.Extract(line,0,p+1,line)
    END
  END nocomments;

  PROCEDURE nospaces;
  CONST twoblanks="  ";
  VAR p,p2:INTEGER; X,X2:ARRAY 131+1 OF CHAR;
  BEGIN
    p := S.Pos('"',line,0);
    IF p >= 0 THEN
      p2 := S.Pos(" DB ",line,0); WHILE line[p2] = ' ' DO DEC(p2) END;
      S.Extract(line,0,p2+1,X);
      S.Extract(line,p,S.Length(line)-p+1,X2);
      COPY(X,line); S.Append(" DB ",line); S.Append(X2,line)
    ELSE
      WHILE S.Pos(twoblanks,line,0) >= 0 DO
        p := S.Pos(twoblanks,line,0);
        IF p >= 0 THEN
          p2 := p;
          WHILE line[p2] = ' ' DO INC(p2) END;
          S.Extract(line,0,p+1,X);
          S.Extract(line,p2,S.Length(line)-p2+1,X2);
          COPY(X,line); S.Append(X2,line)
        END
      END
    END
  END nospaces;

BEGIN (* load *)
  WHILE ~F.Eof(oldasm) DO
    bytesread := F.readline(oldasm,line); INC(linesread);
    IF (S.Length(line) = 0) OR (line[0] = ';') THEN
      DEC(linesread)
    ELSE
      nocomments; nospaces;
      NEW(src[linesread],bytesread); COPY(line,src[linesread]^);
      B.getbytenames(src[linesread]^)
    END
  END
END load;

PROCEDURE doall*(doit:doitproc);
VAR i:INTEGER;
BEGIN FOR i := 0 TO linesread DO doit(src[i]) END
END doall;

PROCEDURE show*(VAR line:pstring);
BEGIN
  IF (line # NIL) & (S.Length(line^) > 0) THEN
    F.WriteString(newasm,line^); F.WriteLn(newasm)
  END
END show;

BEGIN linesread := 0
END loadfile.
/* === loadfile.ob2 ends === */

/* === invober.ob2 begins === */
(*$MAIN+ *)
(* public domain, nenies proprajho, free for any use *)
MODULE invober;
IMPORT B := bytename,L := loadfile,F := Myfiles,S := Str,O := Out;

VAR jumpnum:INTEGER; cseg:BOOLEAN;

PROCEDURE adjust(VAR oldline:L.pstring);
  TYPE str20=ARRAY 20+1 OF CHAR;
  VAR p,p2:INTEGER; line,X,X2:ARRAY 131 OF CHAR; op:str20;
      size:ARRAY 4+1 OF CHAR;

  PROCEDURE find(s:ARRAY OF CHAR):INTEGER;
  BEGIN RETURN S.Pos(s,line,0)
  END find;

  PROCEDURE found(s:ARRAY OF CHAR):BOOLEAN;
  BEGIN p := find(s); RETURN p # -1
  END found;

  PROCEDURE finddel(s:ARRAY OF CHAR);
  BEGIN
    IF found(s) THEN
      S.Extract(line,0,p,X); S.Extract(line,p+S.Length(s),S.Length(line)-(p+S.Length(s))+1,X2);
      line := X; S.Append(X2,line)
    END
  END finddel;

  PROCEDURE replace(no,yes:ARRAY OF CHAR);
  VAR i:INTEGER;
  BEGIN
    IF found(no) THEN
      FOR i := 0 TO S.Length(yes)-1 DO line[p+i] := yes[i] END
    END
  END replace;

  PROCEDURE fixname(myop:str20);
  VAR name:str20; s:ARRAY 2 OF CHAR; i:INTEGER;
  BEGIN
    s := " "; name := myop;
    i := S.Pos('[',name,0);
    IF i >= 0 THEN
      S.Extract(name,0,i,X); COPY(X,name); S.Append('+',X);
      s[0] := myop[S.Length(op)-2]; S.Append(s,X); COPY(X,myop)
    END;
    IF B.isbytename(name) THEN size := "byte" ELSE size := "word" END;
    S.Extract(line,0,p,X); S.Append(size,X); S.Append(" ptr[",X);
    S.Append(myop,X); S.Append(']',X)
  END fixname;

  PROCEDURE fixop1():BOOLEAN;
  CONST tab=9X;
  VAR rc:BOOLEAN;
  BEGIN
    rc := FALSE;
    p := find(','); DEC(p); p2 := p;
    WHILE ~((line[p]=' ') OR (line[p]=tab)) DO DEC(p) END; INC(p);
    S.Extract(line,p,p2-p+1,op);
    IF (S.Length(op) > S.Length("DX")) & ((op[0] >= 'A') & (op[0] <= 'Z')) & (op[0] # '[') THEN
      fixname(op); S.Extract(line,p2+1,S.Length(line)-(p2+1)+1,X2);
      line := X; S.Append(X2,line);
      rc := TRUE
    END;
    RETURN rc
  END fixop1;

  PROCEDURE fixop2;
  BEGIN
    p := find(','); INC(p); p2 := p;
    WHILE (p2 < S.Length(line)) & (line[p2] # ' ') DO INC(p2) END;
    IF line[p2] = ' ' THEN DEC(p2) END;
    S.Extract(line,p,p2-p+1,X);
    IF S.Length(X) > S.Length("DX") THEN
      S.Extract(line,p,p2-p+1,op);
      IF ((op[0] >= 'A') & (op[0] <= 'Z')) & (op[0] # '[') & ((S.Length(op) > 2) & (op[2] # ':')) THEN
        fixname(op); line := X
      END
    END
  END fixop2;

  PROCEDURE incdec;
  BEGIN
    IF found("INC ") OR found("DEC ") THEN
      WHILE line[p] # ' ' DO INC(p) END; WHILE line[p] = ' ' DO INC(p) END;
      p2 := p; WHILE (p2 < S.Length(line)) & (line[p2] # ' ') DO INC(p2) END;
      IF line[p2] = ' ' THEN DEC(p2) END;
      S.Extract(line,p,p2,X);
      IF S.Length(X) > S.Length("DX") THEN
        S.Extract(line,p,p2,op);
        IF ((op[0] >= 'A') & (op[0] <= 'Z')) & (op[0] # '[') THEN
          fixname(op); line := X
        END
      END
    END
  END incdec;

  PROCEDURE fixplusbx;
  BEGIN
    p2 := find('['); S.Extract(line,p2+1,p-p2-1,op);
    IF B.isbytename(op) THEN size := "byte" ELSE size := "word" END;
    S.Extract(line,0,p2,X); S.Append(size,X); S.Append(" ptr",X);
    S.Extract(line,p2,S.Length(line)-p2+1,X2);
    line := X; S.Append(X2,line)
  END fixplusbx;

  PROCEDURE fixlea;
  BEGIN
    replace("LEA ","MOV "); p2 := find(',');
    S.Extract(line,0,p2+1,X); S.Append("OFFSET ",X);
    S.Extract(line,p2+1,S.Length(line)-(p2+1)+1,X2);
    line := X; S.Append(X2,line)
  END fixlea;

  PROCEDURE jmpshort;
  CONST noshort="#$%&,.134578>DGJMOPQRSTUVWXYZ][_^\`abcdefy";
  VAR s:ARRAY 2 OF CHAR;
  BEGIN
    INC(jumpnum); s := " "; s[0] := CHR(jumpnum+ORD(' '));
    IF S.Pos(s,noshort,0) < 0 THEN
      S.Extract(line,0,p+4,X); S.Extract(line,p+4,S.Length(line)-(p+4)+1,X2);
      line := X; S.Append("SHORT ",line); S.Append(X2,line)
    END
  END jmpshort;

  PROCEDURE fixseg;
  BEGIN
    IF found("RemoveNewInt9:") OR found("CLC") THEN cseg := ~cseg END;
    IF cseg & found("MOV ") & ~found("[0") & found("[") THEN
      S.Extract(line,0,p,X); S.Append(" cs:",X);
      S.Extract(line,p,S.Length(line)-p+1,X2);
      line := X; S.Append(X2,line)
    END
  END fixseg;

BEGIN (* adjust *)
  IF (oldline # NIL) & (S.Length(oldline^) > 0) THEN
    COPY(oldline^,line);

    IF found("Assume") OR found("ENDP") THEN line := "" END;

    finddel("[0]"); finddel("Word Ptr ");

    IF found("+BX") THEN
      fixplusbx
    ELSIF found("LEA ") THEN
      fixlea
    ELSIF found(" PROC ") THEN
      S.Extract(line,0,S.Pos(" ",line,0),X); S.Append(':',X); line := X
    ELSIF found("JMP ") THEN
      jmpshort
    END;

    IF ~found(',') THEN
      incdec
    ELSIF ~((found(" DB ") OR found(" DW "))
      OR found(",OF") OR found(",Of") OR found("ASSUME")) THEN
        IF ~fixop1() THEN fixop2 END
    END;

    fixseg; replace("40:","DS:");

    COPY(line,oldline^)
  END
END adjust;

BEGIN (* main *) jumpnum := 0; cseg := FALSE;
  L.oldasm := F.Open("INVADERS.ASM","r");
  IF L.oldasm=NIL THEN O.String("Not found!"); O.Ln; HALT(255) END;
  L.load; O.String("loaded"); O.Ln; F.Close(L.oldasm);
  L.doall(adjust); O.String("adjusted"); O.Ln;

  L.newasm := F.Open("inv.asm","w");
  L.doall(L.show); O.String("written"); O.Ln; F.Close(L.newasm)
END invober.
/* === invober.ob2 ends === */

/* === invober.bat begins === */
@echo off
for %%a in (xds excelsior) do if "%1"=="%%a" goto xds
for %%a in (obc oxford) do if "%1"=="%%a" goto obc
if "%1"=="test" goto test
echo.
echo USAGE: %0 o2 (where "o2" is one of: xds obc)
echo.
goto end
:xds
for %%a in (invober bytename loadfile) do if not exist %%a.ob2 ren %%a.m %%a.ob2
echo on
xc.exe =m invober
@echo off
if not exist invober.exe goto end
echo on
xstrip -q -n invober.exe
@echo off
dir invober.exe | find /i "exe"
goto end
:obc
for %%a in (invober bytename loadfile) do if not exist %%a.m ren %%a.ob2 %%a.m
shift
echo on
obc.exe %1 %2 %3 Str.m Myfiles.m bytename.m loadfile.m invober.m -o invober.exe
@echo off
dir invober.exe | find /i "exe"
goto end
:test
invober.exe
if not exist inv.asm goto end
echo.
echo inv.asm 490BBAA0
crc32 inv.asm
echo.
a86 +ESP0 inv.asm inv-a86 >NUL
jwasmr -q -Zm -bin -Fo=inv-jwas.com inv.asm
wasmr /q /fo=inv-wat inv
warplink /c inv-wat >NUL
tasm /t inv,inv-tasm
warplink /c inv-tasm >NUL
call C:\TMP\INVADR11\MISC\disall2
del inv-*.com >NUL
for %%a in (obj sym lnk k i) do if exist *.%%a del *.%%a
:end
/* === invober.bat ends === */


# --- extract.awk begins ---
#!/usr/bin/awk -f

/[b]egins ===/{
  fname=$3 ; print fname
  while (getline > 0) {
    if ($0 !~ / [e]nds ===/) {
      print > fname
    }
    else {
      close(fname)
      break
    }
  }
}
# --- extract.awk ends ---

------------------------------------------------------------
*** DATA ENDS DATA ENDS DATA ENDS DATA ENDS ***
------------------------------------------------------------
*/

/* EOF */
