diff options
Diffstat (limited to 'kittenc.c')
| -rw-r--r-- | kittenc.c | 548 |
1 files changed, 548 insertions, 0 deletions
diff --git a/kittenc.c b/kittenc.c new file mode 100644 index 0000000..79011ea --- /dev/null +++ b/kittenc.c @@ -0,0 +1,548 @@ +/* + KITTENC - compile kitten files and attach this to + executables in a way that the kitten library retrieves + the language resources, in the same way that + catgets/kittengets ever did + +*/ +/* + This software is free software; free to use, + modify, pass to others, whatever + + use it at your own risk +*/ +/* Minor modifications to compile on UNIX host */ + + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <io.h> + +#include "kitten.h" +#include "kittenc.h" +#include "kitten.c" // Small hack to keep everything in one file + +void usage() + { + printf(kittengets(1,0,"KITTENC - KITTEN compiler\n")); + printf(kittengets(1,1,"usage\n")); + printf(kittengets(1,2,"KITTENC program.exe ATTACH NLS\\program.??\n")); + printf(kittengets(1,3,"KITTENC program.exe ATTACH NLS\\program.DE\n")); + printf(kittengets(1,4,"KITTENC program.exe ATTACH NLS\\program.DE NLS\\program.fr\n")); + printf(kittengets(1,5,"KITTENC program.exe INFO : show info about language resources\n")); + printf(kittengets(1,6,"KITTENC program.exe DUMP : recreate language resources\n")); + printf(kittengets(1,7,"KITTENC program.exe TRUNCATE : delete attached resources\n")); + } + + + +/** + * Parse a string that represents an unsigned integer. + * Returns -1 if an error is found. The first size + * chars of the string are parsed. + */ + +int +mystrtoul (char *src, int base, int size) +{ + int ret = 0; + + for (; size > 0; size--) + { + int digit; + int ch = *src; + + src++; + + if (ch >= '0' && ch <= '9') + digit = ch - '0'; + else if (ch >= 'A' && ch <= 'Z') + digit = ch - 'A' + 10; + else if (ch >= 'a' && ch <= 'z') + digit = ch - 'a' + 10; + else + return -1; + + if (digit >= base) + return -1; + + ret = ret * base + digit; + } /* for */ + + return ret; +} + + + + +/** + * Process strings, converting \n, \t, \v, \b, \r, \f, \\, + * \ddd, \xdd and \x0dd to actual chars. + * (Note: \x is an extension to support hexadecimal) + * This is used to allow the messages to use C escape sequences. + * Modifies the line in-place (always same size or shorter). + * Returns a pointer to input string. + */ + +char special[] = "\\nrtvbafx"; +char translated[]= "\\\n\r\t\v\b\a\fx"; + + +char * +processEscChars (char *line) +{ + /* used when converting \xdd and \ddd (hex or octal) characters */ + char ch; + char *src = line; + char *dst = line; /* possible as dst is shorter than src */ + char *s; + int chx; + + + if (line == NULL) + return line; + + /* cycle through copying characters, except when a \ is encountered. */ + while (*src != '\0') + { + ch = *src; + src++; + + if (ch != '\\') + { + *dst++ = ch; + continue; + } + + ch = *src; /* what follows slash? */ + src++; + + s = strchr(special, ch); /* is it a special character?? */ + + if (s == NULL) /* no. store the character as is */ + { + *dst++ = ch; + continue; + } + ch = translated[s - special]; + + if (ch != 'x') /* newline, formfeed etc. */ + { + *dst++ = ch; + continue; + } + + chx = mystrtoul (src, 16, 2); /* get value */ + if (chx >= 0) + { /* store character */ + *dst = chx; + dst++; + src += 2; + } + else /* error so just store x (loose slash) */ + { + *dst = *src; + dst++; + } + } /* while */ + + /* ensure '\0' terminated */ + *dst = '\0'; + + return line; +} + + +// +// process a single FIND.DE file +// + +int process_language_file(FILE *fd, char *filename) + { + FILE *fdres; + char linebuff[512]; + char msg[512]; + int id1, id2; + struct message_header message_header; + int len; + int linecount = 0; + + if ((fdres = fopen(filename, "r")) == 0) + { + printf("can't open %s\n", filename); + return 0; + } + + while (fgets(linebuff, sizeof(linebuff), fdres)) + { + char *s = linebuff; + + linecount++; + + for (len = strlen(s); len > 0; ) // delete trailing white space + { // from line + len--; + if (strchr(" \t\r\n", s[len]) == NULL) + break; + s[len] = '\0'; + } + if (len == 0) + continue; + + + if (linebuff[0] == '#') + { + // Skip comment line + } + else if (sscanf(linebuff, " %u . %u :%s", &id1, &id2, msg) != 3) + { + if (sscanf(linebuff, " %c", &id1) == 0 || // empty line + id1 == '#') // # comment line + continue; + + printf("%3u:%10.10s: missing 'num, num :' \n", linecount, linebuff); + continue; + } + s = strchr(linebuff, ':'); + if (s == NULL) continue; + + s++; + + processEscChars(s); + + if (strlen(s) > 250) + { + printf("%10.10s: lines must not be longer then 250 characters, is %u\n", linebuff, strlen(s)); + s[250] = 0; + } + + /* now here we have destilled id1, id2, message s */ + + message_header.len = strlen(s) // the message + + 1 // the trailing \0 + + sizeof(message_header); + message_header.id = (id1 << 8) + id2; + + fwrite(&message_header, 1, sizeof(message_header), fd); + fwrite(s, strlen(s)+1,1,fd); + + } + + + message_header.len = 0; // END message + message_header.id = 0; + + fwrite(&message_header, 1, sizeof(message_header), fd); + + return linecount; + } + + +// this is the 'compiler' part of KITTENC +// +// attach("FIND.EXE", "NLS\\FIND.??", 1) +// attach("FIND.EXE", "NLS\\FIND.DE", NLS\\FIND.ES", 2) +// + +int attach(char *progname, char *args[], int argc) + { + int i; + FILE *fd; + struct _finddata_t fileinfo; + long search_handle; + char *s; + char *filename; + char path_buffer[_MAX_PATH]; + struct content content[KITTEN_MAX_RESOURCES]; + struct message_end message_end; + int linecount; + + int resourcecount = 0; + + if ((fd = fopen(progname, "r+b")) == NULL) + { + printf(kittengets(2,1, "executable file <%s> can't be opened. because <%s>\n"), progname, strerror( errno )); + return 1; + } + fseek(fd, 0, SEEK_END); + + for (i = 0; i < argc; i++) + { + filename = args[i]; + +#ifdef __UNIX__ + /* no need to expand wildcards on unix */ + strcpy(path_buffer, filename); +#else + + if ((search_handle = _findfirst(args[i], &fileinfo)) == 0) + { + printf(kittengets(2,2,"%s: no such file\n"), args[i]); + return 1; + } + + do { /* the name is name only, without the path */ + + if ((s = strrchr(filename, '\\')) != NULL || + (s = strrchr(filename, '/')) != NULL || + (s = strrchr(filename, ':')) != NULL) + { + memcpy(path_buffer, filename, s-filename+1); + strcpy(path_buffer + (s-filename) +1, fileinfo.name); + } + else + strcpy(path_buffer, fileinfo.name); + +#endif + + printf("%s:", path_buffer); + + + if ((s = strrchr(path_buffer, '.')) == NULL || + strlen(path_buffer) - (s - path_buffer) != 3) + { + printf(kittengets(2,3,"file names MUST end with .EN\n")); + continue; + } + + if (resourcecount > KITTEN_MAX_RESOURCES) + { + printf(kittengets(2,4,"too many resourcefiles (max %d)\n"), KITTEN_MAX_RESOURCES); + continue; + } + + content[resourcecount].filepos_start = ftell(fd); + strcpy(content[resourcecount].language,s+1); + + + linecount = process_language_file(fd, path_buffer); + + content[resourcecount].filepos_end = ftell(fd); + content[resourcecount].reserved = 0; + + printf("%u messages, %lu byte\n", linecount, content[resourcecount].filepos_end - content[resourcecount].filepos_start); + + resourcecount++; + +#ifndef __UNIX__ + } while (_findnext(search_handle, &fileinfo) == 0); +#endif + } + + message_end.filepos = ftell(fd); + + /* make all absolute filepositions relative to file end + to make UPX support easier */ + + + fwrite(&content, sizeof(content[0]),resourcecount,fd); + + message_end.resource_count = resourcecount; + memcpy(message_end.ID, "KITTENC",8); + + message_end.fileend_orig = ftell(fd) + sizeof(message_end); + + fwrite(&message_end, sizeof(message_end),1,fd); + fclose(fd); + + return 0; + } + +enum { KITTEN_TRUNCATE, KITTEN_INFO, KITTEN_DUMP }; + +do_kitten_stuff(char *progname, int mode) + { + int fd; + struct message_end message_end; + struct content content[KITTEN_MAX_RESOURCES]; + int i; + long fileendnow, seekoffset; + + + + if ((fd = open(progname, _O_RDWR | _O_BINARY)) < 0) + { + printf("can't open %s because<%s>\n", progname, strerror(errno)); + return 0; + } + fileendnow = lseek(fd, 0, SEEK_END); + + lseek(fd, - (int)sizeof(struct message_end), SEEK_END); + + read(fd, &message_end, sizeof(struct message_end)); + + if (memcmp(message_end.ID, "KITTENC", 8) != 0) + { + printf("no KITTENC record found\n"); + goto RETURN; + } + + if (message_end.resource_count > KITTEN_MAX_RESOURCES) + { + printf("resource > %d\n", KITTEN_MAX_RESOURCES); + goto RETURN; + } + + seekoffset = fileendnow - message_end.fileend_orig; + + if (lseek(fd, message_end.filepos+seekoffset, SEEK_SET) != message_end.filepos+seekoffset) + { + printf("can't seek 1\n"); + goto RETURN; + } + + read(fd, &content, sizeof(content[0]) * message_end.resource_count); + + + if (mode == KITTEN_TRUNCATE) + { + _chsize(fd, content[0].filepos_start); // truncate file + goto RETURN; + } + if (mode == KITTEN_INFO) + { + printf("%s: %u languages supported\n", progname, message_end.resource_count); + + for (i = 0; i < message_end.resource_count; i++) + { + printf("%2.2s %lu byte\n", content[i].language, content[i].filepos_end - content[i].filepos_start); + } + + goto RETURN; + } + + if (mode == KITTEN_DUMP) + { + char resname[_MAX_PATH],*s; + char outname[_MAX_PATH]; + FILE *fdout; + int i; + int linecount; + + for (i = 0; i < message_end.resource_count; i++) // for each language + { + _splitpath(progname, NULL,NULL, resname, NULL); // reconstruct KITTENC.DE + + sprintf(outname, "%s.%c%c", resname, content[i].language[0], content[i].language[1]);; + + if ((fdout = fopen(outname, "w")) == NULL) // create KITTENC.DE + { + printf("can't write resourcefile %s\n", outname); + goto RETURN; + } + lseek(fd, content[i].filepos_start+seekoffset, SEEK_SET); + + printf("%s : ", outname); + + linecount= 0; + + while (1) + { + short len, id; char c; + + read(fd, &len, sizeof(short)); + + if (len == 0) + break; + + read(fd,&id, sizeof(short)); + + fprintf(fdout, "%u,%u:", id >> 8, id & 0xff); + + while (read(fd, &c, 1) == 1 && + c != 0) + { + if (c >= 0x20) + fputc(c, fdout); + else + { + if ((s = strchr(translated, c)) != NULL) /* \t \n \f */ + { + fprintf(fdout, "\\%c", special[s - translated]); + } + else + fprintf(fdout, "\\x%02x", c); + } + } // next character + + fprintf(fdout, "\n"); + linecount++; + } // next line + + printf("%u messages\n", linecount); + fclose(fdout); + } // next language + + + goto RETURN; + } + + + +RETURN: + close(fd); + return 0; + + } + + +main(int argc, char *argv[]) +{ + char *progname; + char buffer[_MAX_PATH]; + + kittenopen(argv[0]); + + printf("KITTENC 1.0\n"); + + //printf(kittengets(0,0,"hallo world\n")); + + if (argc < 3) + { + usage(); + exit(1); + } + + progname = argv[1]; +#ifndef __UNIX__ + strupr(progname); + + if (strstr(progname, ".EXE") == NULL && + strstr(progname, ".COM") == NULL) + { + sprintf(buffer,"%s.EXE", progname); + progname = buffer; + } +#endif + + if (stricmp(argv[2], "ATTACH") == 0) + { + do_kitten_stuff(progname, KITTEN_TRUNCATE); + return attach(progname, argv+3, argc-3); + + } + else if (stricmp(argv[2], "TRUNCATE") == 0) + { + return do_kitten_stuff(progname, KITTEN_TRUNCATE); + + + } + else if (stricmp(argv[2], "INFO") == 0) + { + return do_kitten_stuff(progname, KITTEN_INFO); + } + else if (stricmp(argv[2], "DUMP") == 0) + { + return do_kitten_stuff(progname, KITTEN_DUMP); + } + else { + printf("'%s' is wrong %s %s %s\n", argv[2], argv[0], argv[1], argv[2]); + usage(); + exit(1); + } + +} + + |
