/*
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|          Program Name: Bin2C                                                                                          |
|                Author: Michael Krämer                                                                                 |
|                        Michael@dk8pp.de               (private)                                                       |
|                        Michael.Kraemer@eu.necel.com   (business)                                                      |
|                  Date: 13-Apr-2005                                                                                    |
|              Language: Open Watcom C 1.7                                                                              |
|               Version: 1.1                                                                                            |
|        Program status: Modified to use WinLib                                                                         |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|  Purpose: Convert a binary file into C source code                                                                    |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Includes                                                                                                        |
+-----------------------------------------------------------------------------------------------------------------------+
*/
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <commctrl.h>
#include <ctype.h>
#include <time.h>
#include <wavelib.h>
#include <stdint.h>
#include <stdbool.h>
#include "resource.h"
#include "WinLib.h"

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Defines                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
*/
#define STRINGSIZE 256          /* default size for strings */
#define PARMSIZE 20             /* maximum size of a string or positional command line parameter */
#define MAX_LOADSTRING 100
#define BUF_LEN 256             /* general buffer size */
#define INFILENAME_SIZE 50000   /* size for input filename buffer,
                                   multiple files can be selected ! */

/* private messages */
#define USER_THREAD_ENDED 401   /* Thread has ended */

#define MY_HKEY HKEY_CURRENT_USER // handle of registry key
#define MY_INI_SECT "Bin2C" // Section name or registry key used by this program
#define MY_REG_KEY MY_REG_KEY_ROOT MY_INI_SECT // Registry key used by this program
#define PROGRAMNAME "Binary to C-Source Converter"
#define PURPOSE "This program converts binary files to C source code which\r\r\n"\
                "can then be compiled and embedded into the executable.\r\r\n"\
                "\r\r\n"
#define PROGRAMVERSION 110      /* will be displayed as n.nn */

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       structs and enums                                                                                               |
+-----------------------------------------------------------------------------------------------------------------------+
*/

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       global variables                                                                                                |
+-----------------------------------------------------------------------------------------------------------------------+
*/
HINSTANCE       MyInstance ;
HWND            MainWindow ;
static char strng1[STRINGSIZE] ;
static char strng2[STRINGSIZE] ;
static bool sort, toc, dec_wav ;
static char ini_filename[MAX_PATH+1] ;

/*
 Registry definitions
*/

static int32_t registry_x ;             /* left edge of dialog */
static int32_t registry_y ;             /* top edge of dialog */
static int32_t registry_width ;         /* width of dialog */
static int32_t registry_height ;        /* height of dialog */
static uint32_t registry_flags ;        /* store the flags */
static int32_t registry_version ;       /* version of program which wrote the registry */
static char registry_filename[MAX_PATH+1] ; /* filename for registry */
static char registry_binheader[STRINGSIZE+1] = "#pragma ghs section data=\".images\"" ; /* header for binary file */
static char registry_binfooter[STRINGSIZE+1] = "#pragma ghs section data=default" ;     /* footer for binary file */
static char registry_wavheader[STRINGSIZE+1] = "#pragma ghs section data=\".wave\"" ;   /* header for wave file */
static char registry_wavfooter[STRINGSIZE+1] = "#pragma ghs section data=default" ;     /* footer for wave file */
static INIVALUE inivalue[] = {{"Window",   "PosX",      REG_DWORD, REGFLAG_WRITEBACK, &registry_x,        1},
                              {"Window",   "PosY",      REG_DWORD, REGFLAG_WRITEBACK, &registry_y,        1},
                              {"Window",   "Width",     REG_DWORD, REGFLAG_WRITEBACK, &registry_width,    1},
                              {"Window",   "Height",    REG_DWORD, REGFLAG_WRITEBACK, &registry_height,   1},
                              {"Program",  "Version",   REG_DWORD, REGFLAG_NONE,      &registry_version,  1},
                              {"Settings", "Filename",  REG_SZ,    REGFLAG_WRITEBACK, &registry_filename, MAX_PATH},
                              {"Settings", "BinHeader", REG_SZ,    REGFLAG_WRITEBACK, &registry_binheader, STRINGSIZE},
                              {"Settings", "BinFooter", REG_SZ,    REGFLAG_WRITEBACK, &registry_binfooter, STRINGSIZE},
                              {"Settings", "WavHeader", REG_SZ,    REGFLAG_WRITEBACK, &registry_wavheader, STRINGSIZE},
                              {"Settings", "WavFooter", REG_SZ,    REGFLAG_WRITEBACK, &registry_wavfooter, STRINGSIZE},
                              {"Settings", "Flags",     REG_DWORD, REGFLAG_WRITEBACK, &registry_flags,    1} } ;
static MYINI myini = {MY_HKEY, MY_REG_KEY, MY_INI_SECT ".ini", &inivalue[0], INI_NO, sizeof(inivalue)/sizeof(inivalue[0])} ;

/*
 Command line parameter definitions
*/
static char strng[PARMSIZE+1] ; /* string parameter strng */

/* These are indexes into the CLO table below. Keep them in sync */
typedef enum {PARM_RC=0,
              PARM_RD,
              PARM_RR,
              PARM_RW,
              PARM_IF,
              PARM_ID,
              PARM_IR,
              PARM_IW,
              PARM_PP,
              PARM_PT,
              PARM_LAST} parm_enum ;

CLO clo[] = {{"rc", PARM_SIMPLE, NULL, 0, false, "clear registry entries for this program"},
             {"rd", PARM_SIMPLE, NULL, 0, false, "disable use of the registry for this program\r\r\n"
                                            "     (will use .ini file instead unless -id specified"},
             {"rr", PARM_SIMPLE, NULL, 0, false, "read initialization values from registry"},
             {"rw", PARM_SIMPLE, NULL, 0, false, "write initialization values back to registry"},
             {"if", PARM_STRING, ini_filename, MAX_PATH, false, "specify ini filename"},
             {"id", PARM_SIMPLE, NULL, 0, false, "disable use of ini file for this program"},
             {"ir", PARM_SIMPLE, NULL, 0, false, "read initialization values from ini file"},
             {"iw", PARM_SIMPLE, NULL, 0, false, "write initialization values back to ini file\r\r\n"
                                            "     NOTE: avoid using registry and ini-file at the same time\r\r\n"
                                            "     Exception: -rr -iw to export registry to ini-file and\r\r\n"
                                            "                -ir -rw to export ini-file to registry"},
             {"pp", PARM_INT, NULL, 0, false, "set process priority for dialog\r\r\n"
                                       "       (0:idle, 1:normal, 2:high, 3:realtime)"},
             {"pt", PARM_INT, NULL, 0, false, "set priority for processing thread\r\r\n"
                                       "       (0:idle, 1:lowest, 2:below normal, 3:normal,\r\r\n"
                                "        4:above normal, 5:highest, 6:time critical)"} } ;

uint32_t clo_size = PARM_LAST ;

static char errorparm[MAXPARMLENGTH] ;

/*
 Dialog coordinates definitions
*/
COORDS coordinates[] = {{0, 0, 0, 0, 0, 0},  /* first entry is reserved for main dialog */
#ifdef SUSPENDBUTTON
                        {BUT_SUSPEND, 0, 0, 0, 0, MOVE_X|MOVE_Y},
#endif
                        {DLG_IN, 0, 0, 0, 0, RESIZE_X},
                        {BUT_IN, 0, 0, 0, 0, MOVE_X},
                        {DLG_OUT, 0, 0, 0, 0, RESIZE_X},
                        {BUT_OUT, 0, 0, 0, 0, MOVE_X},
                        {BUT_START, 0, 0, 0, 0, MOVE_X|MOVE_Y},
                        {BUT_EXIT, 0, 0, 0, 0, MOVE_X|MOVE_Y},
                        {BUT_INFO, 0, 0, 0, 0, MOVE_X|MOVE_Y},
                        {DLG_STATUS, 0, 0, 0, 0, RESIZE_X|RESIZE_Y|MOVE_Y}
                       } ;
COORDS *sorted_coord[sizeof(coordinates)/sizeof(coordinates[0])-1] ;
uint32_t coord_size = sizeof(coordinates)/sizeof(coordinates[0]) ;
static bool validreg = false ;          /* valid data from registry retrieved */
static bool abort_thread = false ;      /* set true to abort the thread */
static bool thread_suspended = false ;
char purpose[] = PURPOSE ;
static uint32_t mult_files = 0 ;        /* number of files if multiple files specified, 0 if just one file */
static char infilename[INFILENAME_SIZE] ; /* statically allocated!!! */
static char infilename_no_path[256] ;

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Function prototypes                                                                                             |
+-----------------------------------------------------------------------------------------------------------------------+
*/
static BOOL CALLBACK Bin2C(HWND window_handle, unsigned msg, WPARAM wparam, LPARAM lparam ) ;
static int sortieren(const void *a, const void *b) ;
static bool output_binary(char *outfilename, char *path, char *fn_nopath,
                          char **fnames, uint32_t number_of_files) ;
static bool output_wave(char *outfilename, char *path, char *fn_nopath,
                        char **fnames, uint32_t number_of_files) ;
static void make_varname(char *varname, char *filename) ;
static DWORD WINAPI DoOperation(LPVOID lpParam) ;
static void EnableControls(bool enable) ;

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       WinMain                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| MAIN program.                                                                                                         |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
int WINAPI WinMain(HINSTANCE this_inst, HINSTANCE prev_inst, LPSTR cmdline, int cmdshow)
{
uint32_t static volatile rv ;
bool startup ;
   startup = true ;
   errorparm[0] = '\0' ;
   getparms(cmdline, errorparm) ;
   if (clo[PARM_RC].found) clear_registry(&myini) ;
   myini.initype = INI_NO ;
   if (!clo[PARM_RD].found && !clo[PARM_RR].found && !clo[PARM_RW].found && !clo[PARM_IR].found && !clo[PARM_IW].found)
   { /* enable registry read/write if none of the previous parameters was found (as default and for compatibility) */
      myini.initype |= (INI_REGREAD + INI_REGWRITE) ;
   }
   if (clo[PARM_RD].found && !clo[PARM_RR].found && !clo[PARM_RW].found && !clo[PARM_IR].found && !clo[PARM_IW].found && !clo[PARM_ID].found)
   { /* enable ini file read/write if registry is disabled and none of the previous parameters was found
        use "-rd -id" to use neither registry nor ini file */
      myini.initype |= (INI_FILEREAD + INI_FILEWRITE) ;
   }
   if (clo[PARM_RR].found) myini.initype |= INI_REGREAD ;
   if (clo[PARM_RW].found) myini.initype |= INI_REGWRITE ;
   if (clo[PARM_IR].found) myini.initype |= INI_FILEREAD ;
   if (clo[PARM_IW].found) myini.initype |= INI_FILEWRITE ;
   if (clo[PARM_RD].found) myini.initype &= ~(INI_REGREAD + INI_REGWRITE) ;
   if (clo[PARM_ID].found) myini.initype &= ~(INI_FILEREAD + INI_FILEWRITE) ;
   if (clo[PARM_IF].found) myini.inifilename = ini_filename ;
   if (startup)
   { /* start windowed version */
      if (clo[PARM_PP].found) set_process_prio(clo[PARM_PP].value) ;
      MyInstance = this_inst ;
      InitCommonControls() ;
      rv = DialogBox(this_inst, "DLG_BIN2C", 0, (DLGPROC)Bin2C) ;
   }
   return(0);
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Bin2C                                                                                                           |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Processes messages for the DLG_BIN2C dialog                                                                           |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
BOOL CALLBACK Bin2C(HWND window_handle, unsigned msg, WPARAM wparam, LPARAM lparam )
{
OPENFILENAME static infilestruct ;
OPENFILENAME static outfilestruct ;

char static def_inext[] = "Graphics files\0*.png;*.bmp;*.jpg\0"
                          "Audio files\0*.mp2;*.mpa;*.mp3;*.wav\0"
                          "Video files\0*.mpv;*.mpg;*.avi;*.mpeg\0"
                          "All files\0*.*\0" ;

char static def_outext[] = "C Source files\0*.c;*.cpp\0"
                           "All files\0*.*\0" ;

char static buf[BUF_LEN] ;

uint32_t i ;
BOOL retcd ;
DWORD static dwThreadId, dwThrdParam = 1 ;
HANDLE static hThread ;
RECT static rect ;

char *out_msg ;

   retcd = 0 ; /* return 0 if we don't process the message */
   validreg = false ;
   switch(msg)
   {
   case WM_INITDIALOG:
      retcd = 1 ;
      MainWindow = window_handle ;
// Make sure to define the below mentioned icon!
      SendMessage(MainWindow, WM_SETICON, ICON_BIG, (LPARAM) LoadIcon(MyInstance, "MyWinIcon")) ;
//    ShowError(DLG_STATUS) ; /* display last error message */

// save original coordinates of dialog
      SaveCoordinates() ;

      validreg = get_registry(&myini) ; // get registry content

      if (validreg)
      {
         MoveWindow(
         MainWindow,            // handle of window
         registry_x,            // horizontal position
         registry_y,            // vertical position
         registry_width,        // width
         registry_height,       // height
         true);                 // repaint flag
         CheckDlgButton(MainWindow, CB_SORT,   ((registry_flags & 0x00000001) == 0) ? BST_UNCHECKED : BST_CHECKED) ;
         CheckDlgButton(MainWindow, CB_TOC,    ((registry_flags & 0x00000002) == 0) ? BST_UNCHECKED : BST_CHECKED) ;
         CheckDlgButton(MainWindow, CB_DECWAV, ((registry_flags & 0x00000004) == 0) ? BST_UNCHECKED : BST_CHECKED) ;
         SetDlgItemText(MainWindow, DLG_OUT, registry_filename) ;
      }
      else
      {
         SetDlgItemText(MainWindow, DLG_OUT, "_ConvertedBinaries_.c") ;
         CheckDlgButton(MainWindow,CB_SORT,BST_CHECKED) ;
         CheckDlgButton(MainWindow,CB_TOC ,BST_CHECKED) ;
      }
      SetDlgItemText(MainWindow, DLG_IN, "Binary input file(s)") ;

      /* display any illegal parameter */
      if (strlen(errorparm) != 0)
      {
         LoadString(MyInstance, STR_INVALIDPARM, strng1, STRINGSIZE);
         sprintf_s(strng2, STRINGSIZE, strng1, errorparm) ;
         SetDlgItemText(MainWindow, DLG_STATUS, strng2) ;
      }
#ifdef SUSPENDBUTTON
      EnableWindow(GetDlgItem(MainWindow, BUT_SUSPEND), false) ;
#endif
      sprintf_s(strng2, STRINGSIZE, "%s - Version %u.%2.2u", PROGRAMNAME, PROGRAMVERSION/100, PROGRAMVERSION%100) ;
      SendMessage(MainWindow, WM_SETTEXT, 0, (LPARAM) strng2);
   break ;

   case WM_USER:
      retcd = 1 ;
      switch(LOWORD(wparam))
      {
      case USER_THREAD_ENDED:
      // output the results
         LoadString(MyInstance, STR_NORMAL_END, strng1, STRINGSIZE) ;
         out_msg = strng1 ;
/* Thread may have assembled a message. If so, use it */
         if ((void *) lparam != NULL) out_msg = (char *) lparam ;
         SetDlgItemText(MainWindow, DLG_STATUS, out_msg) ;
         CloseHandle(hThread) ;
         hThread = NULL ;
         EnableControls(true) ;
#ifdef SUSPENDBUTTON
         EnableWindow(GetDlgItem(MainWindow, BUT_SUSPEND), false) ;
#endif
      break ;
      }
   break ;

   case WM_COMMAND:
      retcd = 1 ;
      switch( LOWORD( wparam ) )
      {
      case BUT_IN:
         infilename[0] = '\0' ;
         infilestruct.lStructSize = sizeof(OPENFILENAME) ;
         infilestruct.hwndOwner = MainWindow ;
         infilestruct.hInstance = 0 ;
         infilestruct.lpstrFilter = def_inext ;
         infilestruct.lpstrCustomFilter = NULL ;
         infilestruct.nMaxCustFilter = 0 ;
         infilestruct.nFilterIndex = 1 ;
         infilestruct.lpstrFile = infilename ;
         infilestruct.nMaxFile = INFILENAME_SIZE ;
         infilestruct.lpstrFileTitle = NULL ;
         infilestruct.nMaxFileTitle = 0 ;
         infilestruct.lpstrInitialDir = NULL ;//"d:\Development" ;
         infilestruct.lpstrTitle = "Binary input file(s)" ;
         infilestruct.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY
                            | OFN_ALLOWMULTISELECT | OFN_EXPLORER ;
         infilestruct.nFileOffset = 0 ;
         infilestruct.nFileExtension = 0 ;
         infilestruct.lpstrDefExt = NULL ;
         infilestruct.lCustData = 0 ;
         infilestruct.lpfnHook = NULL ;
         infilestruct.lpTemplateName = NULL ;
         if (GetOpenFileName(&infilestruct) != 0)
         {
            i = 0 ;
            mult_files = 0 ;
            while ((infilestruct.lpstrFile[i+0] != '\0')
                && (infilestruct.lpstrFile[i+1] != '\0'))
            {
               while (infilestruct.lpstrFile[i] != '\0') i++ ;
               if (infilestruct.lpstrFile[i+1] != '\0') {i++ ; mult_files++ ;}
            }
            if (mult_files == 0)
               SetDlgItemText(MainWindow, DLG_IN, infilestruct.lpstrFile) ;
            else
            {
               sprintf_s(buf, BUF_LEN, "%s (%u files selected)", infilestruct.lpstrFile, mult_files) ;
               SetDlgItemText(MainWindow, DLG_IN, buf) ;
            }
         }
      break ;

      case DLG_IN:
         if (mult_files == 0)
            GetDlgItemText(MainWindow, DLG_IN, infilename, INFILENAME_SIZE) ;
      break ;

      case BUT_OUT:
         outfilestruct.lStructSize = sizeof(OPENFILENAME) ;
         outfilestruct.hwndOwner = MainWindow ;
         outfilestruct.hInstance = 0 ;
         outfilestruct.lpstrFilter = def_outext ;
         outfilestruct.lpstrCustomFilter = NULL ;
         outfilestruct.nMaxCustFilter = 0 ;
         outfilestruct.nFilterIndex = 1 ;
         outfilestruct.lpstrFile = registry_filename ;
         outfilestruct.nMaxFile = MAX_PATH ;
         outfilestruct.lpstrFileTitle = NULL ;
         outfilestruct.nMaxFileTitle = 0 ;
         outfilestruct.lpstrInitialDir = NULL ;//"d:\Development" ;
         outfilestruct.lpstrTitle = "C Source output file" ;
         outfilestruct.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT ;
         outfilestruct.nFileOffset = 0 ;
         outfilestruct.nFileExtension = 0 ;
         outfilestruct.lpstrDefExt = NULL ;
         outfilestruct.lCustData = 0 ;
         outfilestruct.lpfnHook = NULL ;
         outfilestruct.lpTemplateName = NULL ;
         if (GetSaveFileName(&outfilestruct) != 0)
         {
            SetDlgItemText(MainWindow, DLG_OUT, outfilestruct.lpstrFile) ;
         }
      break ;

      case DLG_OUT:
         GetDlgItemText(MainWindow, DLG_OUT, registry_filename, MAX_PATH) ;
      break ;

      case BUT_INFO:
         sprintf_s(strng1, STRINGSIZE, "%s - Version %u.%2.2u", PROGRAMNAME, PROGRAMVERSION/100, PROGRAMVERSION%100) ;
         PrintProgramInfo(DLG_HELPLIST, strng1, __DATE__, __TIME__) ;
      break ;

      case IDOK:
      case BUT_START:
         /* The start button becomes abort, if the thread is suspended */
         if (thread_suspended)
         {
            abort_thread = true ;
#ifdef SUSPENDBUTTON
            SendMessage(MainWindow, WM_COMMAND, BUT_SUSPEND, 0) ;
#endif
         }
         else
         {
/* Operation is done in a thread. Start it. */
            hThread = CreateThread(
                NULL,                        // no security attributes
                0,                           // use default stack size
                DoOperation,                 // thread function
                &dwThrdParam,                // argument to thread function
                0,                           // use default creation flags
                &dwThreadId);                // returns the thread identifier

        // Check the return value for success.
            if (hThread == NULL)
               LoadString(MyInstance, STR_THREADSTARTERROR, strng1, STRINGSIZE) ;
            else
            { /* Thread started properly */
               LoadString(MyInstance, STR_WORKING, strng1, STRINGSIZE) ;
               EnableControls(false) ;
               thread_suspended = false ;
#ifdef SUSPENDBUTTON
               EnableWindow(GetDlgItem(MainWindow, BUT_SUSPEND), true) ;
#endif
               if (clo[PARM_PT].found) set_thread_prio(hThread, clo[PARM_PT].value) ;
            }
            SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;
         }
      break ;

      case IDABORT:
      case IDCANCEL:
      case BUT_EXIT:
         GetWindowRect(MainWindow, &rect) ; /* get current dialog coordinates */
         registry_x = rect.left ;
         registry_y = rect.top ;
         registry_width = rect.right - rect.left ;
         registry_height = rect.bottom - rect.top ;
         if (registry_x < 0) registry_x = 0 ;
         if (registry_y < 0) registry_y = 0 ;
         registry_flags  = (IsDlgButtonChecked(MainWindow, CB_SORT) == BST_CHECKED) ?   0x00000001 : 0 ;
         registry_flags |= (IsDlgButtonChecked(MainWindow, CB_TOC) == BST_CHECKED) ?    0x00000002 : 0 ;
         registry_flags |= (IsDlgButtonChecked(MainWindow, CB_DECWAV) == BST_CHECKED) ? 0x00000004 : 0 ;
         registry_version = PROGRAMVERSION ;    /* version of program which wrote the registry */
         put_registry(&myini) ;                 /* store the dialog properties in registry */
         EndDialog(MainWindow, true) ;          /* exits the dialog box */
      break ;

      default:
         retcd = 0 ; /* we have not processed this command, so return zero */
      break ;
      }
   break ;

   default:
      retcd = WinLibMessage(msg, wparam, lparam) ; /* WinLib processes some messages */
   break ;
   }

   return(retcd);
} /* Bin2C */

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void EnableControls(bool enable)                                                                                |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Enable or disable some critical controls during execution of thread.                                                  |
| Control gets enabled if enable is true, otherwise it gets disabled.                                                   |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void EnableControls(bool enable)
{
   EnableWindow(GetDlgItem(MainWindow, BUT_START), enable) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       DoOperation(LPVOID lpParam)                                                                                     |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| This thread does whatever this program is supposed to do.                                                             |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
DWORD WINAPI DoOperation(LPVOID lpParam)
{
char static strng_thread1[STRINGSIZE] ;
char *lpMsgBuf ;
char *retcd ;
bool err ;
uint32_t i, j ;
char static **fnames ;  /* array of pointers to filenames */

   retcd = NULL ; /* NULL means no error */

   sort = IsDlgButtonChecked(MainWindow,CB_SORT) ;
   toc = IsDlgButtonChecked(MainWindow,CB_TOC) ;
   dec_wav = IsDlgButtonChecked(MainWindow,CB_DECWAV) ;
   err = false ;
   // clear status display
   LoadString(MyInstance, STR_EMPTY, strng, MAX_LOADSTRING);
   SetDlgItemText(MainWindow, DLG_STATUS, strng) ;

/* collect pointers to filenames */
   if (mult_files != 0)
   {
      fnames = (char **) malloc((mult_files)*sizeof(char *)) ;
      if (fnames == NULL)
      {
         LoadString(MyInstance, STR_MALLOC_FAILED, strng, MAX_LOADSTRING);
         SetDlgItemText(MainWindow, DLG_STATUS, strng) ;
      }
      j = 0 ;
      for (i=0; i<mult_files; i++)
      {
         while (infilename[j] != '\0') j++ ;
         fnames[i] = &infilename[j+1] ;
         j++ ;
      }
      if (sort)
      { /* sort filenames */
         qsort(fnames,
               mult_files,
               sizeof(char *),
               sortieren) ;
      }
   }

   if (!dec_wav) err = output_binary(registry_filename, infilename, infilename_no_path, fnames, mult_files) ;
   else          err = output_wave(registry_filename, infilename, infilename_no_path, fnames, mult_files) ;

   if (!err)
      LoadString(MyInstance, STR_DONE, strng_thread1, MAX_LOADSTRING);
   else
   {
      FormatMessage(
          FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
          NULL,
          GetLastError(),
          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
          (LPTSTR) &lpMsgBuf,
          0,
          NULL) ;
          strcpy_s(strng_thread1, STRINGSIZE, lpMsgBuf) ;
      // Free the buffer.
          LocalFree(lpMsgBuf) ;
   }

   retcd = strng_thread1 ;
   if (fnames != NULL) free(fnames) ;

// Notify main window that the thread is finished
   SendMessage(MainWindow, WM_USER, USER_THREAD_ENDED, (LPARAM) retcd) ;

   return 0;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       int sortieren(const void *a, const void *b)                                                                     |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| For quicksort                                                                                                         |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
int sortieren(const void *a, const void *b)
{
   return(_stricmp(*((const char **) a), *((const char **) b))) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       bool output_wave(char *outfilename, char *path, char *fn_nopath, char **fnames, uint32_t number_of_files)       |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Decode wave file                                                                                                      |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
bool output_wave(char *outfilename, char *path, char *fn_nopath, char **fnames, uint32_t number_of_files)
{
WFILE *infile ;
FILE *outfile ;
bool err ;
uint32_t filenr ;
char static next_filename[MAX_PATH] = "" ;
char static varname[MAX_PATH] = "" ;
uint32_t i, j ;
char *fn ;
int16_t sample ;

   err = false ;
// open output file
   if (fopen_s(&outfile, outfilename, "wt") != 0) err = true ;

   filenr = 0 ;
   if (!err)
   {
      fprintf(outfile, registry_wavheader) ;
      fprintf(outfile, "\n") ;
      do
      {
         if (number_of_files == 0)
            strcpy_s(next_filename, MAX_PATH, path) ;
         else
         {
            sprintf_s(next_filename, MAX_PATH, "%s\\%s",path ,fnames[filenr]) ;
         }
// open next input file
         infile = wfopen(next_filename) ;

         if (infile == NULL) err = true ;

         if (!err)
         {
            fprintf(outfile, "signed short ") ;
            i = 0 ;
            if (number_of_files == 0)
               fn = fn_nopath ;
            else
               fn = fnames[filenr] ;
            make_varname((char *) &varname, fn) ;
            fprintf(outfile, "%s[] = {", varname) ;

            i = 0 ;
            while (!wfeof(infile))
            {
               if (wfgetsample(&sample, infile) != NULL)
               {
                  if (i > 0) fprintf(outfile,",") ;
                  if ((i % 12) != 0)
                     fprintf(outfile," ") ;
                  else
                     fprintf(outfile,"\n        ") ;
                  fprintf(outfile,"%6.1i", sample) ;
                  i++ ;
               }
            }

            fprintf(outfile, "} ;\n\n") ;
            fprintf(outfile,"unsigned long %s_size = sizeof(%s)/sizeof(signed short) ;\n", varname, varname) ;
            fprintf(outfile,"unsigned short %s_BitsPerSample = %u ;\n", varname, infile->BitsPerSample) ;
            fprintf(outfile,"unsigned short %s_Channels = %u ;\n", varname, infile->Channels) ;
            fprintf(outfile,"unsigned long %s_SamplesPerSecond = %u ;\n\n", varname, infile->SamplesPerSecond) ;
            wfclose(infile) ;
            filenr++ ;
         }
      }
      while ((!err) && (filenr < number_of_files)) ;
   }

   if ((!err) && toc)
   {
      fprintf(outfile, "signed short *toc[] ={\n") ;
      for (i=0; i<filenr; i++)
      {
         j = 0 ;
         if (i != 0) fprintf(outfile, ",\n") ;
         fprintf(outfile,"        ") ;
         if (number_of_files == 0)
            fn = fn_nopath ;
         else
            fn = fnames[i] ;
         make_varname((char *) &varname, fn) ;
         fprintf(outfile, "%s", varname) ;
      }
      fprintf(outfile, "} ;\n") ;
      fprintf(outfile, "int toc_size = sizeof(toc)/sizeof(signed short *) ;\n") ;
   }

   if (!err)
   {
      fprintf(outfile, registry_wavfooter) ;
      fprintf(outfile, "\n") ;
   }
   if (outfile != NULL) fclose(outfile) ;
   return(err) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       bool output_binary(char *outfilename, char *path, char *fn_nopath, char **fnames, uint32_t number_of_files)     |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Decode wave file                                                                                                      |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
bool output_binary(char *outfilename, char *path, char *fn_nopath, char **fnames, uint32_t number_of_files)
{
FILE *infile ;
FILE *outfile ;
bool err ;
uint32_t filenr ;
char static next_filename[MAX_PATH] = "" ;
char static varname[MAX_PATH] = "" ;
uint32_t i, j ;
char *fn ;
char next_char ;

   err = false ;
// open output file
   if (fopen_s(&outfile, outfilename, "wt") != 0) err = true ;

   filenr = 0 ;
   if (!err)
   {
      fprintf(outfile, registry_binheader) ;
      fprintf(outfile, "\n") ;
      do
      {
         if (number_of_files == 0)
            strcpy_s(next_filename, MAX_PATH, path) ;
         else
         {
            sprintf_s(next_filename, MAX_PATH, "%s\\%s", path ,fnames[filenr]) ;
         }
// open next input file
         if (fopen_s(&infile, next_filename, "rb") != 0) err = true ;

         if (!err)
         {
            fprintf(outfile, "unsigned char ") ;
            i = 0 ;
            if (number_of_files == 0)
               fn = fn_nopath ;
            else
               fn = fnames[filenr] ;
            make_varname( (char *) &varname, fn) ;
            fprintf(outfile, "%s[] = {", varname) ;

            i = 0 ;
            while (!feof(infile))
            {
               if (fread(&next_char,1,1,infile) == 1)
               {
                  if (i > 0) fprintf(outfile,",") ;
                  if ((i % 16) != 0)
                     fprintf(outfile," ") ;
                  else
                     fprintf(outfile,"\n        ") ;
                  fprintf(outfile,"0x%2.2x", (uint8_t) next_char) ;
                  i++ ;
               }
            }

            fprintf(outfile, "} ;\n\n") ;
            fclose(infile) ;
            filenr++ ;
         }
      }
      while ((!err) && (filenr < number_of_files)) ;
   }

   if ((!err) && toc)
   {
      fprintf(outfile, "unsigned char *toc[] ={\n") ;
      for (i=0; i<filenr; i++)
      {
         j = 0 ;
         if (i != 0) fprintf(outfile, ",\n") ;
         fprintf(outfile,"        ") ;
         if (number_of_files == 0)
            fn = fn_nopath ;
         else
            fn = fnames[i] ;
         make_varname((char *) &varname, fn) ;
         fprintf(outfile, "%s", varname) ;
      }
      fprintf(outfile, "} ;\n") ;
      fprintf(outfile, "int toc_size = sizeof(toc)/sizeof(unsigned char *) ;\n") ;
   }

   if (!err)
   {
      fprintf(outfile, registry_binfooter) ;
      fprintf(outfile, "\n") ;
   }
   if (outfile != NULL) fclose(outfile) ;
   return(err) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void make_varname(char *varname, char *filename)                                                                |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Make valid variable name from filename. Remove special characters.                                                    |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void make_varname(char *varname, char *filename)
{
uint32_t i, j ;
   i = 0 ; j = 0 ;
   while ((filename[i] != '.') && (filename[i] != '\0'))
   {
      if ( ((i == 0) && isalpha(filename[i]))
         ||((i != 0) && isalnum(filename[i])) )
      {
         varname[j] = filename[i] ;
         j++ ;
      }
      i++ ;
   }
   varname[j] = '\0' ;
}

