[Back to EXEC SWAG index] [Back to Main SWAG index] [Original]

    {
    >Hmmm.... how about this.... I want to put a 75k MOD file into the EXE...
    >I've heard that you use pointers and appending the MOD to end of your
    >compiled program and stuff like that... I'm not too sure how to go about
    >it.

    In short, the easiest way is to append to to your .EXE file. The
    following code will search the current .exe for data appended to
    the end of the .exe file.
    }

    Uses
      DOS;

    TYPE              { .exe file header }
      EXEH = RECORD
        id,            { .exe signature }
        Lpage,         { .exe file size mod 512 bytes; < 512 bytes }
        Fpages,        { .exe file size div 512 bytes; + 1 if Lpage > 0 }
        relocitems,    { number of relocation table items }
        size,          { .exe header size in 16-byte paragraphs }
        minalloc,      { min heap required in additional to .exe image }
        maxalloc,      { extra heap desired beyond that required
                         to hold .exe's image }
        ss,            { displacement of stack segment }
        sp,            { initial SP register value }
        chk_sum,       { complemented checksum }
        ip,            { initial IP register value }
        cs,            { displacement of code segment }
        ofs_rtbl,      { offset to first relocation item }
        ovr_num : word; { overlay numbers }
      END;

    CONST
      MAX_BLOCK_SIZE = 65528; {maximum allowable size of data block in
                                TP}
    TYPE
      pdata = ^data_array;
      data_array = array[0..MAX_BLOCK_SIZE] of byte;

      pMODblock = ^MODblock;
      MODblock = RECORD
        data     :pdata;
        datasize :word;
      end;

    VAR
      exefile : file;
      exehdr  : exeh;
      blocks  : word;

      exesize,
      imgsize : longint;

      path    : dirstr;
      name    : namestr;
      ext     : extstr;
      EXEName : pathstr;
      n       : byte;

      dirfile : searchrec;

      M       : pMODblock;

    {Determines the exe filename, opens the file for read-only, and
     determines the actual .exe code image size by reading the
     standard .exe header that is in front of every .exe file. The .MOD
     data will be in the file *after* the end of the code image.}
    Procedure ReadHdr;

      {this "finds" your exe filename}
      Function CalcEXEName : string;
      var
        Dir  : DirStr;
        Name : NameStr;
        Ext  : ExtStr;
      begin
        if Lo(DosVersion) >= 3 then
          EXEName := ParamStr(0)
        else
          EXEName := FSearch('progname.EXE', GetEnv('PATH'));
                             {  ^^^^^^^^ } { change this to intended EXE name }
        FSplit(EXEName, Dir, Name, Ext);
        CalcEXEName := Name;
      end;

    begin
      Name := CalcEXEName;

      findfirst(EXEName, anyfile, dirfile);
      while (doserror=0) do
      BEGIN
        Assign(exefile, EXEName);
        Reset(exefile, 1);         { reset for 1 byte records }
        BlockRead(exefile, exehdr, SizeOf(exehdr), blocks);
        if blocks<SizeOf(exehdr) then
        begin
          Writeln('File read error!');
          Halt(1);
        end;
        exesize := dirfile.size;     { the total file size of exe+data }
        with exehdr do
        begin
          imgsize := FPages; {exe img size div 512 bytes, +1 if Lpage>0}
          if LPage > 0 then
            dec(imgsize);
          imgsize := (imgsize*512) + LPage; {final image size}
        end;
      END;
    end;

    { this function reads the 64k-8 byte sized block, numbered
      "blocknum" from the end of the file exefile (already opened in
      ReadHdr proc above), allocates a new pMODblock structure and
      passes it back to the caller. "blocknum" is 0-based - ie, data
      offset starts at 0. If the remaining data is less than 64k, the
      data record will be sized to the remaining data.}
    Function ReadBlockFromMOD(blocknum):pMODblock;
    var
      filepos : longint;
      mod     : pMODblock;
    begin
      filepos := imgsize + (blocknum*MAX_BLOCK_SIZE);
      if filepos > exesize then {block position asked for exceeds filesize}
      begin
        ReadBlockFromMOD := NIL; { return error signal }
        EXIT;                    {...and return}
      end;
      New(mod);

      if (filepos+MAX_BLOCK_SIZE>exesize) then
        mod^.datasize := exesize-filepos
            { data left in this block is less than 64k }
      else
        mod^.datasize := MAX_BLOCK_SIZE;
            { data block is a full 64k }
      GetMem(mod^.data, mod^.datasize); {get the memory for the data buffer}

      Seek(exefile, filepos); { position dos's filepointer to beginning of block}
      BlockRead(exefile, mod^.data^, mod^.datasize, blocks);

      if blocks<mod^.datasize then { make sure we got all the data }
      begin
        Writeln('File read error!');
        FreeMem(mod^.data, mod^.datasize);
        Dispose(mod);
        ReadBlockFromMOD := NIL;
        EXIT;
      end;

      ReadBlockFromMOD := mod;
    end;

    {
       This will read in the .MOD from the "back" of the .exe 64k-8
       bytes at a time. As written, you manually have to pass a block
       number to the "read" function.

       A couple of caveats - doing it as written is error-prone. Using
       this code "barebones" in a finished application is not advisable,
       but it does demonstrate the concept and gives you a starting
       point. THIS CODE HAS NOT BEEN TESTED! If you have problems with
       it, let me know and I'll help you out.

       After you have digest the code, ask some more questions and we
       can discuss streams and OOP techniques to do this in a less
       dangerous manner.
    }

[Back to EXEC SWAG index] [Back to Main SWAG index] [Original]