/**
 * Bin2Rexx: "DosOpen() / DosRead() / DosWrite() / DosClose()" example.
 */

#include <OS2.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#define WR
#define RC

typedef void * (PTR);
typedef void ** (PPTR);

#define desc struct
#define const enum class

#define Run int main
#define let void

/* * */

ULONG Length (PBYTE String) { return strlen (String); }
BYTE IsLower (BYTE Character) { return islower (Character); }
BYTE ToUpper (BYTE Character) { return toupper (Character); }
let Join (PBYTE WR Target, PBYTE Source) { strcat (Target, Source); }
let Convert (PBYTE WR String, ULONG Number, ULONG Base) { ultoa (Number, String, Base); }

#ifndef STDOUT
#define STDOUT 1
#endif

let Say (PBYTE String) { ULONG Written = 0; DosWrite (STDOUT, String, Length (String), &Written); }
let Say (PBYTE A, PBYTE B) { Say (A); Say (B); }
let Say (PBYTE A, PBYTE B, PBYTE C) { Say (A); Say (B); Say (C); }
let Say (PBYTE A, PBYTE B, PBYTE C, PBYTE D) { Say (A); Say (B); Say (C); Say (D); }
let Say (PBYTE A, PBYTE B, PBYTE C, PBYTE D, PBYTE E) { Say (A); Say (B); Say (C); Say (D); Say (E); }

/* * */

desc REXX
{
  const SCRIPT
  {
    BYTES_TO_JOIN = 16,
    LINE_LENGTH   = 256
  };
};

/* * */

let PrintRexxScript (PBYTE Memory, ULONG Length, PCHAR File_name)
{
  let PrintProlog ()
  {
    Say ("/* * */", "\r\n");
    Say ("\r\n");
    Say ("V = ''", "\r\n");
  }

  let PrintScript (PBYTE Memory, ULONG Length, PCHAR File_name)
  {
    let JoinHexValues (PCHAR WR Line, PBYTE Memory, INT Memory_length, INT Cntr, INT Bytes_to_join)
    {
      for (INT Offset = 0; Offset < Bytes_to_join; Offset ++) 
      {
        INT Position = Cntr + Offset;

        if (Position < Memory_length)
        {
          CHAR Hex_value[4] = {0};

          {
            BYTE Bin_value = (BYTE) Memory[Position]; Convert (Hex_value, Bin_value, 16);

            INT Hex_value_length = Length (Hex_value);

            if (Hex_value_length == 1) 
            {
              Hex_value[2] = 0; Hex_value[1] = Hex_value[0]; Hex_value[0] = '0';
            }

            if (IsLower (Hex_value[0])) Hex_value[0] = ToUpper (Hex_value[0]);
            if (IsLower (Hex_value[1])) Hex_value[1] = ToUpper (Hex_value[1]);
          }

          Join (Line, "'");
          Join (Line, Hex_value);
          Join (Line, "'x");
        }
      }
    }

    for (INT Cntr = 0; Cntr < Length; Cntr ++) 
    {
      CHAR Line[REXX::SCRIPT::LINE_LENGTH] = ""; Join (Line, "V = V || ");

      JoinHexValues (Line, Memory, Length, Cntr, REXX::SCRIPT::BYTES_TO_JOIN);

      Say (Line, "\r\n");

      Cntr += (REXX::SCRIPT::BYTES_TO_JOIN - 1);
    }
  }

  let PrintEpilog (PCHAR File_name)
  {
    Say ("\r\n");
    Say ("File = '", File_name, "'", "\r\n");
    Say ("Call SysFileDelete File", "\r\n");
    Say ("Call CharOut File, V", "\r\n");
    Say ("\r\n");
    Say ("Exit");
  }

  PrintProlog ();
  PrintScript (Memory, Length, File_name);
  PrintEpilog (File_name);
}

/* * */

Run (INT Arg_c, PCHAR Arg_v[])  
{
  if (Arg_c != 2 || !strstr (Arg_v[1], ".")) 
  {
    Say ("Usage: Bin2Rexx Binary-file.ext > Rexx-script.cmd");
  }
  else
  {
    HFILE File   = NULLHANDLE;
    ULONG Action = OPEN_ACTION_OPEN_IF_EXISTS;
    ULONG Mode   = OPEN_SHARE_DENYWRITE |
                   OPEN_ACCESS_READONLY;
    ULONG Report = 0;

    DosOpen (Arg_v[1], &File, &Report, 0, 0, Action, Mode, NULL);

    if (File)
    {
      PBYTE Memory = NULL; ULONG Length = 0; ULONG _ = 0;

      DosSetFilePtr (File, 0, FILE_END, &Length); DosSetFilePtr (File, 0, FILE_BEGIN, &_);
      DosAllocMem ((PPTR) &Memory, Length, PAG_READ | PAG_WRITE | PAG_COMMIT);
      DosRead (File, Memory, Length, &Report);

      PrintRexxScript (Memory, Length, Arg_v[1]);

      DosFreeMem (Memory);
      DosClose (File);
    }
    else
    {
      DosExit (EXIT_PROCESS, -1);
    }
  }

  return 0;
}
