BHX 4.0

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
carlos
Expert
Posts: 503
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

BHX 4.0

#1 Post by carlos » 27 Feb 2014 02:34

This version was deprecated because bugs and the latest version is found here:
http://www.dostips.com/forum/viewtopic.php?f=3&t=6400

BHX 4.0

I left here the rebuild script for the stable version of bhx, a tool for rebuild binary data from batch.
The utility is free and open source.
The source can be found here: http://consolesoft.com/p/bhx

Note: many thanks to foxidrive, npocmaka_, Aacini, Honguito98 and einstein1969 for suggestions, bug reporting or good comments.
Last edited by carlos on 14 Apr 2015 07:30, edited 6 times in total.

carlos
Expert
Posts: 503
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: BHX 3.5.1 [stable version]

#2 Post by carlos » 10 Mar 2014 01:29

I posted a minor update. Now the latest version is 3.5.1

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: BHX 3.5.1 [stable version]

#3 Post by foxidrive » 10 Mar 2014 06:57

Thanks carlos.

carlos
Expert
Posts: 503
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: BHX 4.0 [new stable version]

#4 Post by carlos » 25 May 2014 23:06

I posted a new version. The version 4.0 speedy up so much the creation of bigger files.

For get this I removed the usage of certutil.exe and also I removed the creation of a temporal file with hexadecimal data.
Instead of it, a vbs script read the data directly from the batch script.

The difference is great. See the next table for see the difference in the creation of a file of 308 KB (316317 bytes)

comparation of time of creation of bigger file (308KB)
v4.0 : 2 seconds
v3.5 : 3 minutes 16 seconds (so slow)

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: BHX 4.0 [new stable version]

#5 Post by foxidrive » 26 May 2014 08:56

Magic! Thanks carlos.

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: BHX 4.0

#6 Post by mirrormirror » 03 Mar 2016 17:21

I was attempting to download the source for bhx.exe from the link above:

Code: Select all

The source can be found here: http://consolesoft.com/p/bhx

But the site is now offline. I was wondering if anyone downloaded this while it was available?

ShadowThief
Expert
Posts: 1166
Joined: 06 Sep 2013 21:28
Location: Virginia, United States

Re: BHX 4.0

#7 Post by ShadowThief » 03 Mar 2016 18:42

mirrormirror wrote:I was attempting to download the source for bhx.exe from the link above:

Code: Select all

The source can be found here: http://consolesoft.com/p/bhx

But the site is now offline. I was wondering if anyone downloaded this while it was available?

As the first post says, 4.0 has been deprecated in favor of 5.6: viewtopic.php?f=3&t=6400

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: BHX 4.0

#8 Post by mirrormirror » 03 Mar 2016 19:15

As the first post says, 4.0 has been deprecated in favor of 5.6: viewtopic.php?f=3&t=6400

Yes, i saw that - and have downloaded bhx.exe v5.6 - but the source code was located on the consoesoft site (for the latest version) and I never downloaded it. Just wondering if anyone was able to get it before the site went down.

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: BHX 4.0

#9 Post by sambul35 » 04 Mar 2016 08:35

carlos wrote:I left here the rebuild script for the stable version of bhx, a tool for rebuild binary data from batch.


Would someone explain for non-experienced batchers like me, in what scenarios this tool may be useful? Is it for updating & compressing something like SQL Database after adding new data to it by running BHX batch? What are the advantages of such method compare to other existing? Similar question was posted by another member here, never answered. Understanding what to use this tool for can greatly broaden its user base, so its usually in the open source author's interest to give some practical examples. :wink:

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: BHX 4.0

#10 Post by foxidrive » 04 Mar 2016 09:59

sambul35 wrote:Would someone explain for non-experienced batchers like me, in what scenarios this tool may be useful?


Batch files display readable text, whereas binary files contain control characters and 8 bit characters.

This tool allows someone to include any binary file (image, program, data file or text file) within the batch script, so it can be recreated by someone. One benefit is including a utility that the batch script itself uses, inside the batch script, so no extra files are required.

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: BHX 4.0

#11 Post by mirrormirror » 05 Mar 2016 16:53

Well, I found the code on google cache: http://webcache.googleusercontent.com/s ... clnk&gl=us
So now my next question...
I would love to modify this (and hopefully it is fine with Carlos as he said it is open source) to change the width of the output file - but I don't know C language !
So if anyone has some spare time and would be willing to help with this please let me know. I'd like to be able to pass the output width via a command-line parameter. I'm guessing the code that needs to change is in one or both of these places:

Code: Select all

   for (i = 0; i <= last_offset; ++i) {
       fprintf(fileOutput, "%02X", buffer.data[i]);
       chrs_printed += 2;
       if ((64 == chrs_printed) || (i == last_offset)) {
      fprintf(fileOutput, "\n");
      chrs_printed = 0;
       }
   }

Code: Select all

       if ((60 == chrs_printed) || (i == last_offset)) {
      chrs_printed = 0;
      fprintf(fileOutput, "\n");
       }


The whole thing is here:

Code: Select all

/*
  Copyright (C) 2010-2015 Carlos Montiers Aguilera

  This software is provided 'as-is', without any express or implied
  warranty. In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.

  Carlos Montiers Aguilera
  cmontiers@gmail.com
*/

/*
  BHX v5.6
*/

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <io.h>
#include <conio.h>

#define IS_NOT_CAB (0)
#define CAB_OK (1)
#define CAB_INVALID (2)
#define CAB_NEED_OTHERS_FILES_FOR_EXPAND (3)
#define CAB_FILE_UNKNOW_COMPRESSION (4)
#define CAB_UTF_FILENAMES (5)
#define CAB_FILES_WITH_PATH_PREFIX (6)

#define cfhdrPREV_CABINET 0x0001
#define cfhdrNEXT_CABINET 0x0002
#define cfhdrRESERVE_PRESENT 0x0004
#define ifoldCONTINUED_FROM_PREV (0xFFFD)
#define ifoldCONTINUED_TO_NEXT (0xFFFE)
#define ifoldCONTINUED_PREV_AND_NEXT (0xFFFF)
#define A_NAME_IS_UTF (0x80)

#define COMPRESSION_NONE    (0)
#define COMPRESSION_MSZIP   (1)
#define COMPRESSION_QUANTUM (2)
#define COMPRESSION_LZX     (3)

struct CFHEADER {
    BYTE signature[4];      /* cabinet file signature */
    DWORD reserved1;      /* reserved */
    DWORD cbCabinet;      /* size of this cabinet file in bytes */
    DWORD reserved2;      /* reserved */
    DWORD coffFiles;      /* offset of the first CFFILE entry */
    DWORD reserved3;      /* reserved */
    BYTE versionMinor;      /* cabinet file format version, minor */
    BYTE versionMajor;      /* cabinet file format version, major */
    WORD cFolders;      /* number of CFFOLDER entries in this cabinet */
    WORD cFiles;      /* number of CFFILE entries in this cabinet */
    WORD flags;         /* cabinet file option indicators */
    WORD setID;         /* must be the same for all cabinets in a set */
    WORD iCabinet;      /* number of this cabinet file in a set */

    WORD cbCFHeader;      /* (optional) size of per-cabinet reserved area */
    BYTE cbCFFolder;      /* (optional) size of per-folder reserved area */
    BYTE cbCFData;      /* (optional) size of per-datablock reserved area */
    BYTE *abReserve;      /* (optional) per-cabinet reserved area */
    BYTE *szCabinetPrev;   /* (optional) name of previous cabinet file */
    BYTE *szDiskPrev;      /* (optional) name of previous disk */
    BYTE *szCabinetNext;   /* (optional) name of next cabinet file */
    BYTE *szDiskNext;      /* (optional) name of next disk */
};

struct BUFFER {
    BYTE *data;
    DWORD size;
};

struct TRACKER {
    struct BUFFER *buffer;
    DWORD offset;
    BOOL BufferOverflowAttempt;
};

static char map_enc85[85 + 1] = {
    "0123456789"
    "abcdefghij"
    "klmnopqrst"
    "uvwxyzABCD"
    "EFGHIJKLMN"
    "OPQRSTUVWX"
    "YZ.-:+=^`/"
    "*?&<>()[]{"
    "}~,$#"
};

int bhx(char *pathFile, char *options[], long number_of_options);
char *getFileName(char *);
struct BUFFER file2buffer(char *);
void help(void);
int getCabInfo(struct BUFFER *, struct CFHEADER *);
BOOL canNotIncrement(struct TRACKER *, DWORD);
BOOL skipOffset(struct TRACKER *, DWORD);
BYTE readByte(struct TRACKER *);
WORD readWord(struct TRACKER *);
DWORD readDWord(struct TRACKER *);
DWORD bufferlen(struct TRACKER *);
BOOL bufferHavePathSeparator(struct TRACKER *);
int main(int argc, char *argv[]);

void help(void)
{
    printf("\nBHX v5.6\n"
      "Encode a binary file in a batch script for rebuild it.\n"
      "\n"
      "By default, the generated batch script name is mybin.cmd.\n"
      "Is recommended convert your file in a cabinet\n"
      "with compression, created with this command line:\n"
      "Makecab /d compressiontype=lzx file file.cab\n"
      "and use the generated cabinet as source.\n"
      "This can save many bytes in the output script.\n"
      "\n"
      "BHX source [/ne] [/o:out] [/y]\n"
      "\n"
      "  source  Binary to encode.\n"
      "  /ne     If the source file is a cabinet, not write code\n"
      "          for expand it.\n"
      "  /o:out  out is the output filename instead of mybin.cmd.\n"
      "  /y      Overwrite the output file.\n"
      "  /hex    Encode binary data using 16 hexadecimal characters.\n"
      "          The default encoder uses 85 printable characters.\n"
      "\n"
      "BHX /author\n"
      "  display the author of the program.\n" "\n");
}

int main(int argc, char *argv[])
{
    if (argc == 2) {
   if (!stricmp(argv[1], "/author")) {
       printf
      ("\nBHX was programmed by Carlos Montiers Aguilera.\n\n");
       goto bye;
   }
    }

    if ((argc >= 2) && (argc <= 5)) {
   char *pathFile = argv[1];


#define NUMBER_OF_OPTIONS (4)

   if ((0 != stricmp(pathFile, "/?"))) {
       char *options[NUMBER_OF_OPTIONS] = {
      NULL,
      NULL,
      NULL,
      NULL
       };

       int opt_index = 0;
       int argv_index = 2;

       while (opt_index < NUMBER_OF_OPTIONS) {
      if (argc > (argv_index)) {
          options[opt_index] = argv[argv_index];
      }

      opt_index++;
      argv_index++;
       }

       return bhx(pathFile, options, NUMBER_OF_OPTIONS);

   }

    }

    help();
  bye:
    return 0;

}



char *getFileName(char *filePath)
{
    char *fileName = filePath;
    char *pi = filePath;
    while (*pi) {
   if (('\\' == *pi) || ('/' == *pi)) {
       fileName = pi + 1;
   }
   ++pi;
    }
    return fileName;
}

int bhx(char *pathFile, char *options[], long number_of_options)
{

    char *Sign = "Script made using BHX 5.6 { consolesoft.com/p/bhx }";
    char *ouputFileName = "mybin.cmd";   /* Default output filename */
    char *fileName;
    FILE *fileOutput;
    struct BUFFER buffer;
    struct CFHEADER cab;
    int cabinfo;
    int cabinetNameIsInside = FALSE;

    BOOL HexEncode = FALSE;   /* Use enc85 as default */

    WORD index;
    BYTE *p;

    long i;
    long last_offset;
    UINT num;
    UINT divisor;
    int cnt;
    int chrs_printed;

    int fileOutputExists;
    int ExpandCabinet = TRUE;   /* Generate code for expand cab file */
    int Overwrite = FALSE;   /* Overwrite ouput file */

    int argument_error = FALSE;

    char option_ne[] = { '/', 'n', 'e', '\0' };
    char option_o[] = { '/', 'o', ':', '\0' };
    char option_y[] = { '/', 'y', '\0' };
    char option_hex[] = { '/', 'h', 'e', 'x', '\0' };
#define O_OPTION_PREFIX_LENGTH 3


    for (i = 0; i < number_of_options; ++i) {
   if (NULL != options[i]) {
       if (!stricmp(options[i], option_ne)) {
      ExpandCabinet = FALSE;
       } else if (!stricmp(options[i], option_y)) {
      Overwrite = TRUE;
       } else if (!stricmp(options[i], option_hex)) {
      HexEncode = TRUE;
       } else
      if (!strnicmp(options[i], option_o, O_OPTION_PREFIX_LENGTH)
      ) {
      ouputFileName = options[i] + O_OPTION_PREFIX_LENGTH;
       } else {
      printf("Error: Invalid argument: %s\n", options[i]);
      argument_error = TRUE;
       }
   }
    }

    if (argument_error) {
   return 1;
    }

    buffer = file2buffer(pathFile);

    if (NULL == buffer.data) {
   printf("Error: Source cannot be opened for reading.\n");
   return 1;
    }

    if (0 == buffer.size) {
   printf("Error: Source is empty.\n");
   free(buffer.data);
   return 1;
    }

    fileOutputExists = (-1 != _access(ouputFileName, 0));

    if (fileOutputExists && !Overwrite) {

   /* Prompt for overwrite */
   int goOut = FALSE;
   int reply;

   while (!goOut) {
       printf("Overwrite \"%s\"? (y/n): ", ouputFileName);
       reply = getch();
       printf("%c\n", reply);

       switch (reply) {
       case 'Y':
       case 'y':
      Overwrite = TRUE;
      goOut = TRUE;
      break;
       case 'N':
       case 'n':
      Overwrite = FALSE;
      goOut = TRUE;
      break;

       }
   }

   if (!Overwrite) {
       free(buffer.data);
       return 1;
   }
    }

    fileOutput = fopen(ouputFileName, "w");
    if (NULL == fileOutput) {
   printf("Error: \"%s\" cannot be opened for writing.\n",
          ouputFileName);
   free(buffer.data);
   return 1;
    }

    fileName = getFileName(pathFile);   /* Get fileName */

    cabinfo = getCabInfo(&buffer, &cab);

    /* If is cabinet not preserve attributes of files inside it */
    if (CAB_OK == cabinfo) {
   p = buffer.data + cab.coffFiles;
   for (index = 0; index < cab.cFiles; ++index) {
       p += 14;
       *p++ = 0;      //None attribute
       *p++ = 0;      //None attribute

       if (!cabinetNameIsInside) {
      cabinetNameIsInside = !stricmp((const char *) p, fileName);
       }

       p += strlen((const char *) p) + 1;
   }
    }

    if (IS_NOT_CAB == cabinfo) {
   ExpandCabinet = FALSE;
    }

    if (ExpandCabinet) {
   if (cabinetNameIsInside) {
       ExpandCabinet = FALSE;
       printf
      ("The cabinet have inside a filename equal to the cabinet name.\n"
       "If you want expand it, first rename the cabinet.\n");
   } else if (CAB_OK != cabinfo) {
       ExpandCabinet = FALSE;
       switch (cabinfo) {
       case CAB_INVALID:
      printf("The cabinet archive is corrupted\n");
      break;
       case CAB_NEED_OTHERS_FILES_FOR_EXPAND:
      printf
          ("The cabinet need one or more cabinets for expand it.\n");
      break;
       case CAB_FILE_UNKNOW_COMPRESSION:
      printf
          ("The cabinet have inside files with unknow compression.\n");
      break;
       case CAB_UTF_FILENAMES:
      printf("The cabinet have inside filenames with utf.\n");
      break;
       case CAB_FILES_WITH_PATH_PREFIX:
      printf
          ("The cabinet have inside filenames with one or more path separator.\n");
      break;
       }
   }
   if (!ExpandCabinet) {
       printf("The code for expand it will not be included !\n");
   }
    }

    fprintf(fileOutput,
       "@Echo Off\n"
       "SetLocal EnableExtensions\n"
       "Call :Rebuild\n"
       "If ErrorLevel 1 Echo Rebuild failed.\n"
       "Goto :Eof\n\n"
       ":Rebuild\n"
       "Rem %s\n"
       "SetLocal EnableExtensions EnableDelayedExpansion\n"
       "Set \"bin=%s\"\n"
       "Set /A \"size=%lu\"\n", Sign, fileName, buffer.size);

    fprintf(fileOutput, "For %%%%# In (\n");

    if (ExpandCabinet) {
   p = buffer.data + cab.coffFiles;
   for (index = 0; index < cab.cFiles; ++index) {
       p += 16;
       fprintf(fileOutput, "\"%s\"\n", p);
       p += strlen((const char *) p) + 1;

   }
    }


    fprintf(fileOutput,
       "\"!bin!\" \"!bin!.da\" \"!bin!.tmp\"\n"
       ") Do If Exist \"%%%%#\" (Del /A /F /Q \"%%%%#\" >Nul 2>&1\n"
       "If ErrorLevel 1 Exit /B 1 )\n"
       "Set \"fsrc=%%~f0\"\n"
       "Findstr /B /N \":+res:!bin!:\" \"!fsrc!\" >\"!bin!.tmp\"\n"
       "(Set /P \"inioff=\"\n"
       "Set /P \"endoff=\") <\"!bin!.tmp\"\n"
       "For /F \"delims=:\" %%%%# In (\"!inioff!\") Do Set \"inioff=%%%%#\"\n"
       "For /F \"delims=:\" %%%%# In (\"!endoff!\") Do Set \"endoff=%%%%#\"\n"
       "Set \".=ado=\"adodb.stream\"\"\n"
       "Set \".=!.! :set a=createobject(ado) :a.type=1 :a.open\"\n"
       "Set \".=!.! :set u=createobject(ado) :u.type=2 :u.open\"\n"
       "Set \".=!.! :set fs=createobject(\"scripting.filesystemobject\")\"\n"
       "Set \".=!.! :set s=fs.opentextfile(\"!fsrc!\",1,0,0)\"\n");



    if (HexEncode) {
   fprintf(fileOutput,
      "Set \".=!.! :for i=1 to !inioff! step 1 :s.readline :next\"\n"
      "Set \".=!.! :do while i<!endoff! :d=replace(s.readline,\" \",\"\")\"\n"
      "Set \".=!.! :for j=1 to len(d) step 2\"\n"
      "Set \".=!.! :u.writetext chrb(\"^&h\"&mid(d,j,2))\"\n");
    } else {
   fprintf(fileOutput,
      "Set \".=!.! :e=\"0123456789abcdefghijklmnopqrstuvwxyzABCDEF\"\n"
      "Set \".=!.!GHIJKLMNOPQRSTUVWXYZ.-:+=^^`/*?&<>()[]{}~,$#\"\n"
      "Set \".=!.!\" :max=!size! :wri=0 :n=array(0,0,0,0,0)\"\n"
      "Set \".=!.! :for i=1 to !inioff! step 1 :s.readline :next\"\n"
      "Set \".=!.! :do while i<!endoff! :d=replace(s.readline,\" \",\"\")\"\n"
      "Set \".=!.! :for j=1 to len(d) step 5 :num85=mid(d,j,5)\"\n"
      "Set \".=!.! :v=0 :for k=1 to len(num85) step 1\"\n"
      "Set \".=!.! :v=v*85+instr(1,e,mid(num85,k,1))-1 :next\"\n"
      "Set \".=!.! :n(1)=Fix(v/16777216) :v=v-n(1)*16777216\"\n"
      "Set \".=!.! :n(2)=Fix(v/65536) :v=v-n(2)*65536\"\n"
      "Set \".=!.! :n(3)=Fix(v/256) :n(4)=v-n(3)*256\"\n"
      "Set \".=!.! :for m=1 to 4 step 1 :if (wri < max) then\"\n"
      "Set \".=!.! :u.writetext chrb(n(m)) :wri=wri+1 :end if :next\"\n");
    }

    fprintf(fileOutput,
       "Set \".=!.! :next :i=i+1 :loop\"\n"
       "Set \".=!.! :u.position=2 :u.copyto a :u.close :set u=nothing\"\n"
       "Set \".=!.! :a.savetofile \"!bin!\",2 :a.close :set a=nothing\"\n"
       "Set \".=!.! :s.close :set s=nothing :set fs=nothing\"\n"
       "Echo !.!>\"!bin!.da\"\n"
       "Set \"ret=1\"\n"
       "Cscript.exe /B /E:vbs \"!bin!.da\" >Nul\n"
       "For %%%%# In (\"!bin!\") Do If \"%%%%~z#\"==\"!size!\" Set \"ret=0\"\n");

    if (ExpandCabinet) {
   fprintf(fileOutput,
      "If \"0\"==\"!ret!\" Expand.exe -r \"!bin!\" -F:* . >Nul\n"
      "If ErrorLevel 1 Set \"ret=1\"\n"
      "Del /A /F \"!bin!\" \"!bin!.da\" \"!bin!.tmp\" >Nul\n");
    } else {
   fprintf(fileOutput,
      "If \"1\"==\"!ret!\" If Exist \"!bin!\" Del /A /F \"!bin!\" >Nul\n"
      "Del /A /F \"!bin!.da\" \"!bin!.tmp\" >Nul\n");
    }

    fprintf(fileOutput, "Exit /B !ret!\n\n");
    fprintf(fileOutput, ":+res:%s:\n", fileName);

    last_offset = buffer.size - 1;
    chrs_printed = 0;

    if (HexEncode) {

   for (i = 0; i <= last_offset; ++i) {
       fprintf(fileOutput, "%02X", buffer.data[i]);
       chrs_printed += 2;
       if ((64 == chrs_printed) || (i == last_offset)) {
      fprintf(fileOutput, "\n");
      chrs_printed = 0;
       }
   }
    } else {

   cnt = 0;
   num = 0;
   for (i = 0; i <= last_offset; i++) {

       num = (num * 256) + buffer.data[i];
       cnt++;

       if (i == last_offset) {
      while (cnt < 4) {   //padding
          num = (num * 256);
          cnt++;
      }
       }

       if (4 == cnt) {

      divisor = 85 * 85 * 85 * 85;
      while (divisor) {
          fprintf(fileOutput, "%c",
             map_enc85[num / divisor % 85]);
          divisor /= 85;
      }
      chrs_printed += 5;

      cnt = 0;
      num = 0;
       }

       if ((60 == chrs_printed) || (i == last_offset)) {
      chrs_printed = 0;
      fprintf(fileOutput, "\n");
       }

   }

    }

    fprintf(fileOutput, ":+res:%s:\n", fileName);

    fclose(fileOutput);

    printf("\"%s\" generated.\n", ouputFileName);

    free(buffer.data);
    return 0;

}


struct BUFFER file2buffer(char *fileName)
{
    FILE *file;
    struct BUFFER buffer;

    buffer.data = NULL;
    buffer.size = 0;

    file = fopen(fileName, "rb");

    if (NULL != file) {
   /* Set position to end of file */
   fseek(file, 0L, SEEK_END);

   /* Set BUFFER.size to size of the file */
   buffer.size = ftell(file);

   /* Set position to beginning of file */
   fseek(file, 0L, SEEK_SET);

   /* Add 1 for allow null terminator */
   buffer.data = (BYTE *) malloc(1 + buffer.size);

   if (NULL != buffer.data) {
       fread(buffer.data, 1, buffer.size, file);
       if (ferror(file)) {
      free(buffer.data);
      buffer.data = NULL;
      buffer.size = 0;
      printf("Error: Reading the file.\n");

       } else {
      buffer.data[buffer.size] = '\0';   /* Prevent buffer overflow */
       }
   } else {
       buffer.size = 0;
       printf("Error: Out of memory.\n");
   }
   fclose(file);
    }
    return buffer;
}

int getCabInfo(struct BUFFER *buffer, struct CFHEADER *cfheader)
{

    DWORD MINIMAL_CAB_SIZE = 62;
    int buffer_have_cabinet_header;
    char CABINET_HEADER[] = { 'M', 'S', 'C', 'F', '\0', '\0', '\0', '\0' };
    int cabinet_header_size = (int) sizeof(CABINET_HEADER);
    WORD compress_type;
    WORD attribs;
    WORD iFolder;
    WORD i;
    DWORD sl;
    struct TRACKER tracker;

    tracker.buffer = buffer;
    tracker.offset = 0;
    tracker.BufferOverflowAttempt = FALSE;


#define checkOverflowAttempt() if (tracker.BufferOverflowAttempt) { goto ret_CAB_INVALID; }

    if (buffer->size < MINIMAL_CAB_SIZE) {
   return IS_NOT_CAB;
    }

    for (i = 0; i < cabinet_header_size; ++i) {
   buffer_have_cabinet_header = CABINET_HEADER[i] == buffer->data[i];
   if (!buffer_have_cabinet_header) {
       return IS_NOT_CAB;
   }
    }

    /* CFHEADER.signature */
    skipOffset(&tracker, 4 * sizeof(BYTE));
    checkOverflowAttempt();

    /* CFHEADER.reserved1 */
    skipOffset(&tracker, sizeof(DWORD));
    checkOverflowAttempt();

    /* CFHEADER.cbCabinet */
    cfheader->cbCabinet = readDWord(&tracker);
    checkOverflowAttempt();

    if (cfheader->cbCabinet != buffer->size) {
   return CAB_INVALID;
    }

    /* CFHEADER.reserved2 */
    skipOffset(&tracker, sizeof(DWORD));
    checkOverflowAttempt();

    /* CFHEADER.coffFiles */
    cfheader->coffFiles = readDWord(&tracker);
    checkOverflowAttempt();

    /* CFHEADER.reserved3 */
    skipOffset(&tracker, sizeof(DWORD));
    checkOverflowAttempt();

    /* CFHEADER.versionMinor */
    cfheader->versionMinor = readByte(&tracker);
    checkOverflowAttempt();

    /* CFHEADER.versionMajor */
    cfheader->versionMajor = readByte(&tracker);
    checkOverflowAttempt();

    /* CFHEADER.cFolders */
    cfheader->cFolders = readWord(&tracker);
    checkOverflowAttempt();

    if (cfheader->cFolders < 1) {
   return CAB_INVALID;
    }

    /* CFHEADER.cFiles */
    cfheader->cFiles = readWord(&tracker);
    checkOverflowAttempt();

    if (cfheader->cFiles < 1) {
   return CAB_INVALID;
    }

    /* CFHEADER.flags */
    cfheader->flags = readWord(&tracker);
    checkOverflowAttempt();

    /* CFHEADER.setID */
    cfheader->setID = readWord(&tracker);
    checkOverflowAttempt();

    /* CFHEADER.iCabinet */
    cfheader->iCabinet = readWord(&tracker);
    checkOverflowAttempt();

    /* (cfhdrRESERVE_PRESENT | cfhdrPREV_CABINET | cfhdrNEXT_CABINET) = 7 */
    if (cfheader->flags > 7) {
   return CAB_INVALID;
    }

    cfheader->cbCFHeader = (WORD) 0;
    cfheader->cbCFFolder = (BYTE) 0;
    cfheader->cbCFData = (BYTE) 0;
    cfheader->abReserve = NULL;
    cfheader->szCabinetPrev = NULL;
    cfheader->szDiskPrev = NULL;
    cfheader->szCabinetNext = NULL;
    cfheader->szDiskNext = NULL;

    if (cfheader->flags & cfhdrRESERVE_PRESENT) {
   /* CFHEADER.cbCFHeader */
   cfheader->cbCFHeader = readWord(&tracker);
   checkOverflowAttempt();

   /* CFHEADER.cbCFFolder */
   cfheader->cbCFFolder = readByte(&tracker);
   checkOverflowAttempt();

   /* CFHEADER.cbCFData */
   cfheader->cbCFData = readByte(&tracker);
   checkOverflowAttempt();

   if (cfheader->cbCFHeader) {
       /* Not saving abReserve offset */

       /* cbCFHeader is the size of CFHEADER->abReserve */
       skipOffset(&tracker, cfheader->cbCFHeader);
       checkOverflowAttempt();
   }

    }


    if (cfheader->flags & cfhdrPREV_CABINET) {
   cfheader->szCabinetPrev = tracker.buffer->data + tracker.offset;
   sl = bufferlen(&tracker);
   checkOverflowAttempt();
   skipOffset(&tracker, sl + 1);
   checkOverflowAttempt();

   cfheader->szDiskPrev = tracker.buffer->data + tracker.offset;
   sl = bufferlen(&tracker);
   checkOverflowAttempt();
   skipOffset(&tracker, sl + 1);
   checkOverflowAttempt();
    }

    if (cfheader->flags & cfhdrNEXT_CABINET) {
   cfheader->szCabinetNext = tracker.buffer->data + tracker.offset;
   sl = bufferlen(&tracker);
   checkOverflowAttempt();
   skipOffset(&tracker, sl + 1);
   checkOverflowAttempt();

   cfheader->szDiskNext = tracker.buffer->data + tracker.offset;
   sl = bufferlen(&tracker);
   checkOverflowAttempt();
   skipOffset(&tracker, sl + 1);
   checkOverflowAttempt();
    }

    for (i = 1; i <= cfheader->cFolders; ++i) {
   /* CFFOLDER.coffCabStart */
   skipOffset(&tracker, sizeof(DWORD));
   checkOverflowAttempt();

   /* CFFOLDER.cCFData */
   skipOffset(&tracker, sizeof(WORD));
   checkOverflowAttempt();

   /* CFFOLDER.typeCompress: (1st BYTE)type */
   compress_type = (WORD) readByte(&tracker) & (WORD) 0xF;
   checkOverflowAttempt();

   switch (compress_type) {
   default:
       return CAB_FILE_UNKNOW_COMPRESSION;
   case COMPRESSION_NONE:
   case COMPRESSION_MSZIP:
   case COMPRESSION_QUANTUM:
   case COMPRESSION_LZX:
       break;
   }

   /* CFFOLDER.typeCompress: (2nd BYTE)memory */
   skipOffset(&tracker, sizeof(BYTE));
   checkOverflowAttempt();

   /* CFFOLDER.abReserve */
   skipOffset(&tracker, cfheader->cbCFFolder);
   checkOverflowAttempt();

    }


    for (i = 1; i <= cfheader->cFiles; ++i) {

   skipOffset(&tracker, sizeof(DWORD));   /* CFFILE.cbFile */
   checkOverflowAttempt();
   skipOffset(&tracker, sizeof(DWORD));   /* CFFILE.uoffFolderStart */
   checkOverflowAttempt();

   iFolder = readWord(&tracker);   /* CFFILE.iFolder */
   checkOverflowAttempt();

   switch (iFolder) {
   case ifoldCONTINUED_FROM_PREV:
   case ifoldCONTINUED_TO_NEXT:
   case ifoldCONTINUED_PREV_AND_NEXT:
       return CAB_NEED_OTHERS_FILES_FOR_EXPAND;
   }


   /* CFFILE.date */
   skipOffset(&tracker, sizeof(WORD));
   checkOverflowAttempt();

   /* CFFILE.time */
   skipOffset(&tracker, sizeof(WORD));
   checkOverflowAttempt();

   /* CFFILE.attribs */
   attribs = readWord(&tracker);
   checkOverflowAttempt();

   if (attribs & A_NAME_IS_UTF) {
       return CAB_UTF_FILENAMES;
   }

   sl = bufferlen(&tracker);
   checkOverflowAttempt();

   if (bufferHavePathSeparator(&tracker)) {
       return CAB_FILES_WITH_PATH_PREFIX;
   }

   /* CFFILE.szName */
   skipOffset(&tracker, sl + 1);
   checkOverflowAttempt();

    }

    return CAB_OK;

  ret_CAB_INVALID:
    return CAB_INVALID;

}

/* Before call this, ensure that tracker.buffer.data have a null terminator */
BOOL bufferHavePathSeparator(struct TRACKER * tracker)
{
    BYTE *b = tracker->buffer->data + tracker->offset;

    while (*b) {
   switch (*b) {
   case '\\':
   case '/':
       return TRUE;
   }
   ++b;
    }

    return FALSE;

}

DWORD bufferlen(struct TRACKER * tracker)
{
    DWORD off = tracker->offset;
    DWORD length = 0;
    BYTE b;

    for (;;) {
   b = readByte(tracker);
   if (tracker->BufferOverflowAttempt) {
       break;
   }
   if ((BYTE) '\0' == b) {
       break;
   }

   ++(length);

   /* check if we not found the nul terminator at the end of buffer.data */
   if (tracker->offset == (tracker->buffer->size - 1)) {
       tracker->BufferOverflowAttempt = TRUE;
       break;
   }

    };

    /* Restoring offset that modify readByte */
    tracker->offset = off;

    return length;
}

BOOL canNotIncrement(struct TRACKER * tracker, DWORD increment)
{
    tracker->BufferOverflowAttempt = FALSE;

    /* overflow */
    if ((tracker->offset) >= (tracker->buffer->size)) {
   tracker->BufferOverflowAttempt = TRUE;
    }

    /* overflow */
    if ((tracker->offset + increment) > (tracker->buffer->size)) {
   tracker->BufferOverflowAttempt = TRUE;
    }

    /* overrun */
    if ((tracker->offset + increment) <= (tracker->offset)) {
   if (increment) {
       tracker->BufferOverflowAttempt = TRUE;
   }
    }

    return tracker->BufferOverflowAttempt;

}

BOOL skipOffset(struct TRACKER * tracker, DWORD increment)
{

    if (canNotIncrement(tracker, increment)) {
   return FALSE;
    }

    tracker->offset += increment;
    return TRUE;
}

/* if BufferOverflowAttempt is set to TRUE the return value is 0 */
BYTE readByte(struct TRACKER * tracker)
{
    BYTE *p;
    BYTE value;

    if (canNotIncrement(tracker, sizeof(BYTE))) {
   return (BYTE) 0;
    }

    p = &(tracker->buffer->data[tracker->offset]);
    value = ((BYTE) (*p));

    tracker->offset += sizeof(BYTE);

    return value;
}


/* if BufferOverflowAttempt is set to TRUE the return value is 0 */
WORD readWord(struct TRACKER * tracker)
{
    BYTE *p;
    WORD value;

    if (canNotIncrement(tracker, sizeof(WORD))) {
   return (WORD) 0;
    }

    p = &(tracker->buffer->data[tracker->offset]);
    value = ((WORD) (*p++));   /* Add 1st byte */
    value |= (((WORD) (*p)) << 8);   /* Add 2nd byte */

    tracker->offset += sizeof(WORD);


    return value;
}


/* if BufferOverflowAttempt is set to TRUE the return value is 0 */
DWORD readDWord(struct TRACKER * tracker)
{
    BYTE *p;
    DWORD value;

    if (canNotIncrement(tracker, sizeof(DWORD))) {
   return (DWORD) 0;
    }

    p = &(tracker->buffer->data[tracker->offset]);
    value = ((DWORD) (*p++));   /* Add 1st byte */
    value |= (((DWORD) (*p++)) << 8);   /* Add 2nd byte */
    value |= (((DWORD) (*p++)) << 16);   /* Add 3nd byte */
    value |= (((DWORD) (*p)) << 24);   /* Add 4th byte */

    tracker->offset += sizeof(DWORD);

    return value;
}

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: BHX 4.0

#12 Post by foxidrive » 05 Mar 2016 18:01

mirrormirror wrote:I would love to modify this (and hopefully it is fine with Carlos as he said it is open source) to change the width of the output file - but I don't know C language !


Thanks for the source code, mirrormirror.

I found a hexedit to change the width of the encoded data as a single byte. Is 255 large enough?

Byte: D5E changes the line width

It's hex 3C by default (3C is 60 characters)
If you patch it to 55 then you get 85 characters
If you patch it to FF then you get 255 characters

You might find a script to patch the byte for you, if that suits what you need to do.

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: BHX 4.0

#13 Post by mirrormirror » 05 Mar 2016 18:11

I think 255 would work for now - and you are talking about modifying the EXE not the source code, correct? But it would be nice to eventually modify the source so that I could pass the width as a param. I'm also wondering if there was any technical reason why the length was 60 - and if changing it might break something...

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: BHX 4.0

#14 Post by foxidrive » 05 Mar 2016 23:16

mirrormirror wrote:I think 255 would work for now - and you are talking about modifying the EXE not the source code, correct


Yes, it's changing a byte in the bhx.exe file itself.

Here's a script that will patch your BHX.EXE file to any number of columns that you need,
and using no parameter will reset the bhx.exe file to the default 60 columns.

It's easier for me to do that than figure out how to add a switch in carlos's code. :)

%1 is the number of columns in decimal (1-255)



Code: Select all

@echo off
:: patch bhx.exe V5.6 at offset D5E for the width of the encoded text
:: That byte controls the number of columns of text from 1 to 255

:: %1 is the number_of_columns

set "bhx=bhx.exe"

for %%a in (%bhx%) do if not %%~za EQU 12288 (
   echo %bhx% seems to be the wrong version - requires V5.6& pause & goto :EOF
)

:: No parameter resets the value to 60 columns
if "%~1"=="" %0 60

:: test for valid input
for /f "delims=1234567890" %%a in ("%~1") do (
   echo value out of range [1-255]& pause & goto :EOF
)
if %~1 LSS 1   %0 a
if %~1 GTR 255 %0 a

set file="%temp%\patchbhx.vbs"
 >%file% echo Dim StdIn, StdOut
>>%file% echo Set StdIn = WScript.StdIn
>>%file% echo Set StdOut = WScript.StdOut
>>%file% echo If WScript.Arguments.Count = 1 Then
>>%file% echo    Do While Not StdIn.AtEndOfStream
>>%file% echo      n = n + 1
>>%file% echo      If n = 3423 Then
>>%file% echo        StdOut.Write Chr("&H"+Hex(WScript.Arguments(0)))
>>%file% echo        StdIn.Read(1)
>>%file% echo       else
>>%file% echo        StdOut.Write (StdIn.Read(1))
>>%file% echo      End If
>>%file% echo    Loop
>>%file% echo End If
cscript /nologo %file% %~1 <%bhx% >%bhx%.1
move /y %bhx%.1 %bhx% >nul
del %file%  2>nul



I'm also wondering if there was any technical reason why the length was 60 - and if changing it might break something...


In my small sample of tests the regenerated files were all binary duplicates, but the reason why 60 was chosen could have been for Usenet posts in newsgroups, where long lines often wrap in newsreaders.

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: BHX 4.0

#15 Post by mirrormirror » 06 Mar 2016 16:37

Here's a script that will patch your BHX.EXE file to any number of columns that you need,
and using no parameter will reset the bhx.exe file to the default 60 columns.

Well this is great - I know everyone's time is valuable and the time you spent on this is appreciated - thank you. It seems to work fine for what I need - I'll test it further this week.

Post Reply