#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mhmt-types.h"
#include "mhmt-parsearg.h"
#include "mhmt-globals.h"
struct argtbl default_arg_table[] =
{
{"d", ARG_MODE, NULL},
{"g", ARG_GREEDY, NULL},
{ARGSTR_MEGALZ, ARG_PTYPE, NULL},
{ARGSTR_HRUM, ARG_PTYPE, NULL},
{ARGSTR_HRUST, ARG_PTYPE, NULL},
{ARGSTR_ZX7, ARG_PTYPE, NULL},
{"zxh", ARG_ZXHEAD, NULL},
{ARGSTR_8, ARG_WORD, NULL},
{ARGSTR_16, ARG_WORD, NULL},
{"bend", ARG_BIGEND, NULL},
{ARGSTR_MW256, ARG_MAXWIN, NULL},
{ARGSTR_MW512, ARG_MAXWIN, NULL},
{ARGSTR_MW1024, ARG_MAXWIN, NULL},
{ARGSTR_MW2048, ARG_MAXWIN, NULL},
{ARGSTR_MW2176, ARG_MAXWIN, NULL},
{ARGSTR_MW4096, ARG_MAXWIN, NULL},
{ARGSTR_MW4352, ARG_MAXWIN, NULL},
{ARGSTR_MW8192, ARG_MAXWIN, NULL},
{ARGSTR_MW16384, ARG_MAXWIN, NULL},
{ARGSTR_MW32768, ARG_MAXWIN, NULL},
{ARGSTR_MW65536, ARG_MAXWIN, NULL},
{ARGSTR_PB, ARG_PREBIN, NULL},
{NULL, 0, NULL}
};
// main argument parser, sets fields of "struct globals wrk",
// tries to detect all erroneous arguments.
ULONG parse_args(int argc, char* argv[])
{
struct argtbl argstore[ARG_STORE_SIZE+1]; // last element is always stop-value
struct argtbl * arg;
ULONG inarg_pos; // position in argv[] array
ULONG storearg_pos; // position in argstore[] array
ULONG files_num; // number of filenames specified
char * temp_filename;
ULONG last_arg_type;
ULONG maxwin;
ULONG i;
for(i=0;i<ARG_STORE_SIZE+1;i++)
{
argstore[i].name = NULL;
argstore[i].fname = NULL;
argstore[i].type = ARG_NOARG;
}
if( argc<2 )
{
printf("No arguments! Use \"mhmt -h\" or \"mhmt -help\" for help!\n");
return 0L;
}
// shortcut for help request
if( !cmp_str_nocase( argv[1]+1, "h" ) || !cmp_str_nocase( argv[1]+1, "help" ) )
return ARG_PARSER_SHOWHELP;
inarg_pos = 1;
storearg_pos = 0;
files_num = 0;
// first find all arguments beginning with "-"
while( inarg_pos<(ULONG)argc && *(argv[inarg_pos])=='-' )
{
arg=match_arg(argv[inarg_pos]+1); // search match...
if( arg ) // match!
{
if( storearg_pos>=ARG_STORE_SIZE )
{
printf("Too many arguments!\n");
return ARG_PARSER_ERROR|ARG_PARSER_SHOWHELP;
}
argstore[storearg_pos] = *arg;
if( arg->type == ARG_PREBIN )
{
if( inarg_pos>=(ULONG)(argc-1) )
{
printf("\"-prebin\" has no filename!\n");
return ARG_PARSER_ERROR|ARG_PARSER_SHOWHELP;
}
inarg_pos++;
argstore
[storearg_pos
].
fname = (char *)malloc( 1+strlen(argv
[inarg_pos
]) );
if( !argstore[storearg_pos].fname )
{
printf("Cannot allocate memory for filename string!\n");
return ARG_PARSER_ERROR;
}
strcpy( argstore
[storearg_pos
].
fname, argv
[inarg_pos
] );
}
storearg_pos++;
}
else // argument does not match predefined set
{
return ARG_PARSER_ERROR|ARG_PARSER_SHOWHELP;
}
inarg_pos++;
}
// parse filenames then
while( inarg_pos<(ULONG)argc )
{
if( files_num>=2 ) // there should be no more than two filenames
{
printf("Too many filenames specified!\n");
return ARG_PARSER_ERROR|ARG_PARSER_SHOWHELP;
}
temp_filename
= (char *)malloc( 1+strlen(argv
[inarg_pos
]) );
if( !temp_filename )
{
printf("Cannot allocate memory for filename string!\n");
return ARG_PARSER_ERROR;
}
strcpy( temp_filename
, argv
[inarg_pos
] );
if( files_num==0 )
wrk.fname_in = temp_filename;
else // only files_num==1, because of condition in the beginning of current "while" cycle
wrk.fname_out = temp_filename;
files_num++;
inarg_pos++;
}
if( !files_num ) // there must be at least 1 filename specified
{
printf("No filenames specified!\n");
return ARG_PARSER_ERROR|ARG_PARSER_SHOWHELP;
}
// now optional arguments (starting with "-") are stored in argstore[],
// all needed filenames are also copied, go proceed configuring with
// optional arguments
// sort argument array (in increasing .type order) to ensure correct parsing
sort_args( argstore, ARG_STORE_SIZE );
storearg_pos = 0;
last_arg_type = ARG_INIT; // there is no such value in argstore[].type
while( argstore[storearg_pos].type != ARG_NOARG )
{
if( last_arg_type == argstore[storearg_pos].type )
{
printf("Redundant arguments!\n");
return ARG_PARSER_ERROR|ARG_PARSER_SHOWHELP;
}
switch( argstore[storearg_pos].type )
{
case ARG_MODE:
wrk.mode = 1; // set depack mode
break;
case ARG_GREEDY:
if( wrk.mode ) // since sorted, argument list causes parsing go from up to down in this "case" list
{
printf("No greedy mode specification for DEpacking!\n");
return ARG_PARSER_ERROR;
}
wrk.greedy = 1; // set greedy packing mode
break;
case ARG_PTYPE:
if( !cmp_str_nocase( argstore[storearg_pos].name, ARGSTR_MEGALZ ) )
{
wrk.packtype = PK_MLZ;
wrk.zxheader = 0;
wrk.wordbit = 0;
wrk.bigend = 0;
wrk.fullbits = 0;
wrk.maxwin = 4352;
}
else if( !cmp_str_nocase( argstore[storearg_pos].name, ARGSTR_HRUM ) )
{
wrk.packtype = PK_HRM;
wrk.zxheader = 0; // by default, there is NO ZX-HEADER if only -hrm or -hst specified
wrk.wordbit = 1;
wrk.bigend = 0;
wrk.fullbits = 1;
wrk.maxwin = 4096;
}
else if( !cmp_str_nocase( argstore[storearg_pos].name, ARGSTR_HRUST ) )
{
wrk.packtype = PK_HST;
wrk.zxheader = 0; // by default, there is NO ZX-HEADER if only -hrm or -hst specified
wrk.wordbit = 1;
wrk.bigend = 0;
wrk.fullbits = 1;
wrk.maxwin = 65536;
}
else if( !cmp_str_nocase( argstore[storearg_pos].name, ARGSTR_ZX7 ) )
{
wrk.packtype = PK_ZX7;
wrk.zxheader = 0;
wrk.wordbit = 0;
wrk.bigend = 0;
wrk.fullbits = 0;
wrk.maxwin = 2176;
}
else // there shouldn't be this case, but nevertheless...
{
printf("Impossible error #1! Press any key to continue or \"SPACE\" to exit... :-)\n");
return ARG_PARSER_ERROR;
}
break;
case ARG_ZXHEAD:
// ZX-header is not applicable for PK_MLZ type...
// also wrk.packtype has been already set before...
if( PK_MLZ==wrk.packtype )
{
printf("There couldn't be zx-header in megalz mode!\n");
return ARG_PARSER_ERROR;
}
wrk.zxheader = 1;
break;
case ARG_WORD:
// whether bits must be grouped in words or in bytes
if( !cmp_str_nocase( argstore[storearg_pos].name, ARGSTR_8 ) )
{
if( wrk.zxheader ) // won't force byte-wise bits when there is a zx-header
{
printf("There can be only 16bit grouping of bits when ZX-header is active!\n");
return ARG_PARSER_ERROR;
}
wrk.wordbit = 0;
}
else if( !cmp_str_nocase( argstore[storearg_pos].name, ARGSTR_16 ) )
{
wrk.wordbit = 1;
}
else // there shouldn't be this case, but nevertheless...
{
printf("Impossible error #2! Press any key to continue or \"SPACE\" to exit... :-)\n");
return ARG_PARSER_ERROR;
}
break;
case ARG_BIGEND:
// whether word-grouped bits must be big- or little-endian arranged
if( wrk.zxheader )
{
printf("There can be only little-endian arrangement of bits when ZX-header is active!\n");
return ARG_PARSER_ERROR;
}
wrk.bigend = 1;
break;
case ARG_MAXWIN:
maxwin = get_maxwin( argstore[storearg_pos].name );
if( !maxwin ) // there shouldn't be this case, but nevertheless...
{
printf("Impossible error #3! Press any key to continue or \"SPACE\" to exit... :-)\n");
return ARG_PARSER_ERROR;
}
// wrk.maxwin is already initialized to the maximum value suitable for given packing type, so check new setting
if( maxwin > wrk.maxwin )
{
printf("Maximum window specified is too big for given packing type!\n");
return ARG_PARSER_ERROR;
}
wrk.maxwin = maxwin;
break;
case ARG_PREBIN:
wrk.prebin = 1;
wrk.fname_prebin = argstore[storearg_pos].fname;
break;
default:
// once again impossible error: we shouldn't be here since "while" loop condition...
printf("Impossible error #4! Press any key to continue or \"SPACE\" to exit... :-)\n");
return ARG_PARSER_ERROR;
break;
}
last_arg_type = argstore[storearg_pos++].type;
}
return ARG_PARSER_GO;
}
// sort arguments
void sort_args( struct argtbl * args, ULONG argsize )
{
struct argtbl temp;
LONG i,j;
// simple bubble sort since there are not too many arguments
for( i=(argsize-2); i>=0; i-- )
{
for( j=0; j<=i; j++ )
{
if( args[j].type > args[j+1].type )
{
temp = args[j];
args[j] = args[j+1];
args[j+1] = temp;
}
}
}
}
// get maxwin string into number, returns 0 if no match
LONG get_maxwin( char * txtmaxwin )
{
static char * strings[] =
{
ARGSTR_MW256,
ARGSTR_MW512,
ARGSTR_MW1024,
ARGSTR_MW2048,
ARGSTR_MW4096,
ARGSTR_MW4352,
ARGSTR_MW8192,
ARGSTR_MW16384,
ARGSTR_MW32768,
ARGSTR_MW65536,
NULL
};
static LONG sizes[] =
{
256,
512,
1024,
2048,
4096,
4352,
8192,
16384,
32768,
65536,
0
};
ULONG i;
i=0;
while( strings[i] )
{
if( !cmp_str_nocase( strings[i], txtmaxwin ) )
{
return sizes[i];
}
i++;
}
return 0;
}
// finds matching arg in default_arg_table,
// returns ptr to the found element or NULL if not found
struct argtbl * match_arg(char * argument)
{
struct argtbl * test_arg = default_arg_table;
while( test_arg->name && cmp_str_nocase(test_arg->name,argument) )
test_arg++;
return (test_arg->name)?test_arg:NULL;
}
// compares two char strings, ignoring case (uppercase by default)
// returns 0, if equal, -1 if left lower than right, and +1 otherwise
LONG cmp_str_nocase(char * left, char * right)
{
LONG order=0;
UBYTE leftchar,rightchar;
UBYTE leftadd,rightadd;
do
{
leftchar = (UBYTE)*left;
rightchar = (UBYTE)*right;
leftadd = 0;
rightadd = 0;
left++;
right++;
if( leftchar >= (UBYTE)'a' ) leftadd = (UBYTE)('A'-'a');
if( rightchar >= (UBYTE)'a' ) rightadd = (UBYTE)('A'-'a');
if( leftchar > (UBYTE)'z' ) leftadd = 0;
if( rightchar > (UBYTE)'z' ) rightadd = 0;
leftchar += leftadd;
rightchar += rightadd;
if( leftchar<rightchar ) order = (-1);
if( leftchar>rightchar ) order = (+1);
} while( (!order) && leftchar && rightchar );
return order;
}