From db16f0f2095a444d108270fa39a1770dc7637eef Mon Sep 17 00:00:00 2001 From: OBattler Date: Mon, 8 Jul 2024 03:18:26 +0200 Subject: [PATCH] Add LZW support for Teledisk 1.x advanced compressed files. --- src/floppy/CMakeLists.txt | 3 + src/floppy/fdd_td0.c | 31 ++- src/floppy/lzw/CMakeLists.txt | 16 ++ src/floppy/lzw/lzw.h | 49 ++++ src/floppy/lzw/lzwdecode.c | 269 ++++++++++++++++++++ src/floppy/lzw/lzwencode.c | 454 ++++++++++++++++++++++++++++++++++ src/floppy/lzw/lzwlocal.h | 63 +++++ 7 files changed, 876 insertions(+), 9 deletions(-) create mode 100644 src/floppy/lzw/CMakeLists.txt create mode 100644 src/floppy/lzw/lzw.h create mode 100644 src/floppy/lzw/lzwdecode.c create mode 100644 src/floppy/lzw/lzwencode.c create mode 100644 src/floppy/lzw/lzwlocal.h diff --git a/src/floppy/CMakeLists.txt b/src/floppy/CMakeLists.txt index 6d69d2d59..5aea98714 100644 --- a/src/floppy/CMakeLists.txt +++ b/src/floppy/CMakeLists.txt @@ -16,3 +16,6 @@ add_library(fdd OBJECT fdd.c fdc.c fdc_magitronic.c fdc_monster.c fdc_pii15xb.c fdi2raw.c fdd_common.c fdd_86f.c fdd_fdi.c fdd_imd.c fdd_img.c fdd_pcjs.c fdd_mfm.c fdd_td0.c) + +add_subdirectory(lzw) +target_link_libraries(86Box lzw) diff --git a/src/floppy/fdd_td0.c b/src/floppy/fdd_td0.c index 46e29343b..0bf4b1c71 100644 --- a/src/floppy/fdd_td0.c +++ b/src/floppy/fdd_td0.c @@ -42,6 +42,7 @@ #include <86box/fdd_86f.h> #include <86box/fdd_td0.h> #include <86box/fdc.h> +#include "lzw/lzw.h" #define BUFSZ 512 /* new input buffer */ #define TD0_MAX_BUFSZ (1024UL * 1024UL * 4UL) @@ -124,7 +125,9 @@ typedef struct td0_t { uint8_t xdf_ordered_pos[256][2]; uint8_t interleave_ordered_pos[256][2]; + uint8_t *lzw_buf; uint8_t *imagebuf; + uint8_t *processed_buf; } td0_t; @@ -650,11 +653,20 @@ td0_initialize(int drive) head_count = header[9]; if (header[0] == 't') { - td0_log("TD0: File is compressed\n"); - disk_decode.fdd_file = dev->fp; - state_init_Decode(&disk_decode); - disk_decode.fdd_file_offset = 12; - state_Decode(&disk_decode, dev->imagebuf, TD0_MAX_BUFSZ); + if (((header[4] / 10) % 10) == 2) { + td0_log("TD0: File is compressed (TeleDisk 2.x, LZHUF)\n"); + disk_decode.fdd_file = dev->fp; + state_init_Decode(&disk_decode); + disk_decode.fdd_file_offset = 12; + state_Decode(&disk_decode, dev->imagebuf, TD0_MAX_BUFSZ); + } else { + td0_log("TD0: File is compressed (TeleDisk 1.x, LZW)\n"); + if (fseek(dev->fp, 12, SEEK_SET) == -1) + fatal("td0_initialize(): Error seeking to offet 12\n"); + if (fread(dev->lzw_buf, 1, file_size - 12, dev->fp) != (file_size - 12)) + fatal("td0_initialize(): Error reading LZW-encoded buffer\n"); + LZWDecodeFile((char *) dev->imagebuf, (char *) dev->lzw_buf, NULL, file_size - 12); + } } else { td0_log("TD0: File is uncompressed\n"); if (fseek(dev->fp, 12, SEEK_SET) == -1) @@ -1224,10 +1236,9 @@ td0_load(int drive, char *fn) /* Allocate the processing buffers. */ i = 1024UL * 1024UL * 4UL; - dev->imagebuf = (uint8_t *) malloc(i); - memset(dev->imagebuf, 0x00, i); - dev->processed_buf = (uint8_t *) malloc(i); - memset(dev->processed_buf, 0x00, i); + dev->lzw_buf = (uint8_t *) calloc(1, i); + dev->imagebuf = (uint8_t *) calloc(1, i); + dev->processed_buf = (uint8_t *) calloc(1, i); if (!td0_initialize(drive)) { td0_log("TD0: Failed to initialize\n"); @@ -1268,6 +1279,8 @@ td0_close(int drive) d86f_unregister(drive); + if (dev->lzw_buf) + free(dev->lzw_buf); if (dev->imagebuf) free(dev->imagebuf); if (dev->processed_buf) diff --git a/src/floppy/lzw/CMakeLists.txt b/src/floppy/lzw/CMakeLists.txt new file mode 100644 index 000000000..bc6f0b612 --- /dev/null +++ b/src/floppy/lzw/CMakeLists.txt @@ -0,0 +1,16 @@ +# +# 86Box A hypervisor and IBM PC system emulator that specializes in +# running old operating systems and software designed for IBM +# PC systems and compatibles from 1981 through fairly recent +# system designs based on the PCI bus. +# +# This file is part of the 86Box distribution. +# +# CMake build script. +# +# Authors: David Hrdlička, +# +# Copyright 2020-2021 David Hrdlička. +# + +add_library(lzw STATIC lzwdecode.c lzwencode.c) \ No newline at end of file diff --git a/src/floppy/lzw/lzw.h b/src/floppy/lzw/lzw.h new file mode 100644 index 000000000..26abbf6ad --- /dev/null +++ b/src/floppy/lzw/lzw.h @@ -0,0 +1,49 @@ +/*************************************************************************** +* Header for Lempel-Ziv-Welch Encoding and Decoding Library +* +* File : lzw.h +* Purpose : Provides prototypes for functions that use Lempel-Ziv-Welch +* coding to encode/decode files. +* Author : Michael Dipperstein +* Date : January 30, 2004 +* +**************************************************************************** +* +* LZW: An ANSI C Lempel-Ziv-Welch Encoding/Decoding Routines +* Copyright (C) 2005, 2007, 2014 by +* Michael Dipperstein (mdipperstein@gmail.com) +* +* This file is part of the lzw library. +* +* The lzw library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 3 of the +* License, or (at your option) any later version. +* +* The lzw library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +* General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +***************************************************************************/ + +#ifndef _LZW_H_ +#define _LZW_H_ + +/*************************************************************************** +* CONSTANTS +***************************************************************************/ + +/*************************************************************************** +* PROTOTYPES +***************************************************************************/ + /* encode inFile */ +int LZWEncodeFile(char *dest, char *src, uint64_t *dst_len, uint64_t src_len); + +/* decode inFile*/ +int LZWDecodeFile(char *dest, char *src, uint64_t *dst_len, uint64_t src_len); + +#endif /* ndef _LZW_H_ */ diff --git a/src/floppy/lzw/lzwdecode.c b/src/floppy/lzw/lzwdecode.c new file mode 100644 index 000000000..7634b52f8 --- /dev/null +++ b/src/floppy/lzw/lzwdecode.c @@ -0,0 +1,269 @@ +/*************************************************************************** +* Lempel-Ziv-Welch Decoding Functions +* +* File : lzwdecode.c +* Purpose : Provides a function for decoding Lempel-Ziv-Welch encoded +* file streams +* Author : Michael Dipperstein +* Date : January 30, 2005 +* +**************************************************************************** +* +* LZW: An ANSI C Lempel-Ziv-Welch Encoding/Decoding Routines +* Copyright (C) 2005, 2007, 2014, 2017 by +* Michael Dipperstein (mdipperstein@gmail.com) +* +* This file is part of the lzw library. +* +* The lzw library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 3 of the +* License, or (at your option) any later version. +* +* The lzw library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +* General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +***************************************************************************/ + +/*************************************************************************** +* INCLUDED FILES +***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "lzw.h" +#include "lzwlocal.h" + +/*************************************************************************** +* TYPE DEFINITIONS +***************************************************************************/ +typedef struct +{ + uint8_t suffixChar; /* last char in encoded string */ + uint16_t prefixCode; /* code for remaining chars in string */ +} decode_dictionary_t; + +/*************************************************************************** +* CONSTANTS +***************************************************************************/ + +/*************************************************************************** +* MACROS +***************************************************************************/ + +/*************************************************************************** +* GLOBAL VARIABLES +***************************************************************************/ + +/* dictionary of string the code word is the dictionary index */ +static decode_dictionary_t dictionary[(MAX_CODES - FIRST_CODE)]; + +/*************************************************************************** +* PROTOTYPES +***************************************************************************/ +static uint8_t DecodeRecursive(unsigned int code, char **dest); + +/* read encoded data */ +static int GetCodeWord(char *src); + +static uint16_t bufPos = 0x0000; +static uint16_t bufLen = 0x0000; + +static uint32_t bufOutPos = 0x00000000; + +/*************************************************************************** +* FUNCTIONS +***************************************************************************/ + +/*************************************************************************** +* Function : LZWDecodeFile +* Description: This routine reads an input file 1 encoded string at a +* time and decodes it using the LZW algorithm. +* Parameters : fpIn - pointer to the open binary file to decode +* fpOut - pointer to the open binary file to write decoded +* output +* Effects : fpIn is decoded using the LZW algorithm with CODE_LEN codes +* and written to fpOut. Neither file is closed after exit. +* Returned : 0 for success, -1 for failure. errno will be set in the +* event of a failure. +***************************************************************************/ +int +LZWDecodeFile_Internal(char *dest, char *src) +{ + uint16_t nextCode; /* value of next code */ + uint16_t lastCode; /* last decoded code word */ + int code; /* code word to decode */ + uint8_t c; /* last decoded character */ + + /* validate arguments */ + if (dest == NULL) { + errno = ENOENT; + return -1; + } + + bufPos = 0x0000; + bufOutPos = 0x00000000; + + /* initialize for decoding */ + nextCode = FIRST_CODE; /* code for next (first) string */ + + /* first code from file must be a character. use it for initial values */ + lastCode = GetCodeWord(src); + c = lastCode; + *(dest++) = lastCode; + bufOutPos++; + + /* decode rest of file */ + while ((int)(code = GetCodeWord(src)) != EOF) { + if (code < nextCode) { + /* we have a known code. decode it */ + c = DecodeRecursive(code, &dest); + } else { + /*************************************************************** + * We got a code that's not in our dictionary. This must be due + * to the string + char + string + char + string exception. + * Build the decoded string using the last character + the + * string from the last code. + ***************************************************************/ + unsigned char tmp; + + tmp = c; + c = DecodeRecursive(lastCode, &dest); + *(dest++) = tmp; + bufOutPos++; + } + + /* if room, add new code to the dictionary */ + if (nextCode < MAX_CODES) { + dictionary[nextCode - FIRST_CODE].prefixCode = lastCode; + dictionary[nextCode - FIRST_CODE].suffixChar = c; + nextCode++; + } + + /* save character and code for use in unknown code word case */ + lastCode = code; + } + + return 0; +} + +int +LZWDecodeFile(char *dest, char *src, uint64_t *dst_len, uint64_t src_len) +{ + uint16_t size = 0x0000; + uint64_t pos = 0x0000000000000000ULL; + + /* validate arguments */ + if ((dest == NULL) || (src == NULL)) { + errno = ENOENT; + return -1; + } + + if (dst_len != NULL) + *dst_len = 0x0000000000000000ULL; + + while (1) { + size = *(uint16_t *) src; + src += 2; + bufLen = size; + size >>= 1; + if (bufLen & 1) + size++; + if (size > 0x1800) + return -1; + LZWDecodeFile_Internal(dest, src); + src += size; + dest += bufOutPos; + if (dst_len != NULL) + *dst_len += bufOutPos; + pos += (size + 2); + if ((size < 0x1800) || (pos >= src_len)) + /* We have just decoded a block smaller than 0x3000 bytes, + this means this has been the last block, end. */ + break; + } + + return 0; +} + +/*************************************************************************** +* Function : DecodeRecursive +* Description: This function uses the dictionary to decode a code word +* into the string it represents and write it to the output +* file. The string is actually built in reverse order and +* recursion is used to write it out in the correct order. +* Parameters : code - the code word to decode +* fpOut - the file that the decoded code word is written to +* Effects : Decoded code word is written to a file +* Returned : The first character in the decoded string +***************************************************************************/ +static uint8_t +DecodeRecursive(unsigned int code, char **dest) +{ + unsigned char c; + unsigned char firstChar; + + if (code >= FIRST_CODE) { + /* code word is string + c */ + c = dictionary[code - FIRST_CODE].suffixChar; + code = dictionary[code - FIRST_CODE].prefixCode; + + /* evaluate new code word for remaining string */ + firstChar = DecodeRecursive(code, dest); + } else { + /* code word is just c */ + c = code; + firstChar = code; + } + + *((*dest)++) = c; + bufOutPos++; + return firstChar; +} + +/*************************************************************************** +* Function : GetCodeWord +* Description: This function reads and returns a code word from an +* encoded file. In order to deal with endian issue the +* code word is read least significant byte followed by the +* remaining bits. +* Parameters : fpIn - file containing the encoded data +* codeLen - number of bits in code word +* Effects : code word is read from encoded input +* Returned : The next code word in the encoded file. EOF if the end +* of file has been reached. +* +* NOTE: If the code word contains more than 16 bits, this routine should +* be modified to read in all the bytes from least significant to +* most significant followed by any left over bits. +***************************************************************************/ +static int +GetCodeWord(char *src) +{ + int code = 0; + static unsigned int realPos; + + realPos = bufPos >> 1; + + if (bufPos >= bufLen) + /* End of buffer. */ + code = EOF; + else if (bufPos & 1) + /* Odd position. */ + code = (((uint8_t) src[realPos] & 0xf0) >> 4) | ((uint8_t) src[realPos + 1] << 4); + else + /* Even position. */ + code = ((uint8_t) src[realPos] & 0xff) | (((uint8_t) src[realPos + 1] & 0xf) << 8); + + bufPos += 3; + + return code; +} diff --git a/src/floppy/lzw/lzwencode.c b/src/floppy/lzw/lzwencode.c new file mode 100644 index 000000000..0a4d659bb --- /dev/null +++ b/src/floppy/lzw/lzwencode.c @@ -0,0 +1,454 @@ +/*************************************************************************** +* Lempel-Ziv-Welch Encoding Functions +* +* File : lzwencode.c +* Purpose : Provides a function for Lempel-Ziv-Welch encoding of file +* streams +* Author : Michael Dipperstein +* Date : January 30, 2005 +* +**************************************************************************** +* +* LZW: An ANSI C Lempel-Ziv-Welch Encoding/Decoding Routines +* Copyright (C) 2005, 2007, 2014, 2017 by +* Michael Dipperstein (mdipperstein@gmail.com) +* +* This file is part of the lzw library. +* +* The lzw library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 3 of the +* License, or (at your option) any later version. +* +* The lzw library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +* General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +***************************************************************************/ + +/*************************************************************************** +* INCLUDED FILES +***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "lzw.h" +#include "lzwlocal.h" + +/*************************************************************************** +* TYPE DEFINITIONS +***************************************************************************/ +/* node in dictionary tree */ +typedef struct dict_node_t +{ + unsigned int codeWord; /* code word for this entry */ + unsigned char suffixChar; /* last char in encoded string */ + unsigned int prefixCode; /* code for remaining chars in string */ + + /* pointer to child nodes */ + struct dict_node_t *left; /* child with < key */ + struct dict_node_t *right; /* child with >= key */ +} dict_node_t; + +/*************************************************************************** +* CONSTANTS +***************************************************************************/ + +/*************************************************************************** +* MACROS +***************************************************************************/ + +/*************************************************************************** +* GLOBAL VARIABLES +***************************************************************************/ + +/*************************************************************************** +* PROTOTYPES +***************************************************************************/ + +/* dictionary tree node create/free */ +static dict_node_t *MakeNode(const unsigned int codeWord, const unsigned int prefixCode, + const unsigned char suffixChar); +static void FreeTree(dict_node_t *node); + +/* searches tree for matching dictionary entry */ +static dict_node_t *FindDictionaryEntry(dict_node_t *root, const int unsigned prefixCode, + const unsigned char c); + +/* makes key from prefix code and character */ +static unsigned int MakeKey(const unsigned int prefixCode, const unsigned char suffixChar); + +/* write encoded data */ +static int PutCodeWord(char *dest, int code); + +static char *src_base; +static uint64_t src_length = 0x0000000000000000ULL; + +static uint32_t bufPos = 0x00000000; +static uint32_t bufInPos = 0x00000000; + +static int +is_eob(char *src) +{ + return ((uint64_t) (uintptr_t) (src - src_base)) >= src_length; +} + +static int +get_char(char **src) +{ + int ret = EOF; + + if (!is_eob(*src)) { + ret = (uint8_t) **src; + (*src)++; + } + + return ret; +} + +/*************************************************************************** +* FUNCTIONS +***************************************************************************/ + +static int +LZWEncodeFile_Internal(char *dest, char *src) +{ + unsigned int code; /* code for current string */ + unsigned int nextCode; /* next available code index */ + int c; /* character to add to string */ + + dict_node_t *dictRoot; /* root of dictionary tree */ + dict_node_t *node; /* node of dictionary tree */ + + /* validate arguments */ + if (src == NULL) { + errno = ENOENT; + return -1; + } + + /* initialize dictionary as empty */ + dictRoot = NULL; + + nextCode = FIRST_CODE; /* code for next (first) string */ + + bufPos = 0x00000000; + bufInPos = 0x00000000; + + /* now start the actual encoding process */ + + c = get_char(&src); + + if (c == EOF) + return -1; /* empty file */ + else { + bufInPos++; + code = c; /* start with code string = first character */ + } + + /* create a tree root from 1st 2 character string */ + if ((c = get_char(&src)) != EOF) { + bufInPos++; + + /* special case for NULL root */ + dictRoot = MakeNode(nextCode, code, c); + + if (dictRoot == NULL) { + perror("Making Dictionary Root"); + return -1; + } + + nextCode++; + + /* write code for 1st char */ + (void) PutCodeWord(dest, code); + + /* new code is just 2nd char */ + code = c; + } + + /* now encode normally */ + while ((c = get_char(&src)) != EOF) { + /* look for code + c in the dictionary */ + node = FindDictionaryEntry(dictRoot, code, c); + + if ((node->prefixCode == code) && (node->suffixChar == c)) + /* code + c is in the dictionary, make it's code the new code */ + code = node->codeWord; + else { + /* code + c is not in the dictionary, add it if there's room */ + if (nextCode < MAX_CODES) { + dict_node_t *tmp = MakeNode(nextCode, code, c); + + if (tmp == NULL) { + perror("Making Dictionary Node"); + FreeTree(dictRoot); + return -1; + } + + nextCode++; + + if (MakeKey(code, c) < MakeKey(node->prefixCode, node->suffixChar)) + node->left = tmp; + else + node->right = tmp; + } + + /* write out code for the string before c was added */ + if (PutCodeWord(dest, code)) + break; + + /* new code is just c */ + code = c; + } + + bufInPos++; + } + + /* no more input. write out last of the code. */ + (void) PutCodeWord(dest, code); + + /* free the dictionary */ + FreeTree(dictRoot); + + return (c == EOF) ? 1 : 0; +} + +/*************************************************************************** +* Function : LZWEncodeFile +* Description: This routine reads an input file 1 character at a time and +* writes out an LZW encoded version of that file. +* Parameters : fpIn - pointer to the open binary file to encode +* fpOut - pointer to the open binary file to write encoded +* output +* Effects : fpIn is encoded using the LZW algorithm with CODE_LEN codes +* and written to fpOut. Neither file is closed after exit. +* Returned : 0 for success, -1 for failure. errno will be set in the +* event of a failure. +***************************************************************************/ +int +LZWEncodeFile(char *dest, char *src, uint64_t *dst_len, uint64_t src_len) +{ + uint64_t pos = 0x0000000000000000ULL; + + /* validate arguments */ + if ((dest == NULL) || (src == NULL)) { + errno = ENOENT; + return -1; + } + + if (dst_len != NULL) + *dst_len = 0x0000000000000000ULL; + + src_base = src; + src_length = src_len; + + while (1) { + int ret = LZWEncodeFile_Internal(dest + 2, src); + if (ret == -1) + break; + *(uint16_t *) dest = bufPos; + if (bufPos & 1) + bufPos = (bufPos >> 1) + 1; + else + bufPos >>= 1; + dest += (bufPos + 2); + if (dst_len != NULL) + *dst_len += (bufPos + 2); + /* TODO: Why do we need this - 1 clunkfest? */ + src += bufInPos; + pos += bufInPos; + if ((ret == 1) || (pos >= src_len) || (bufPos < 0x1800)) + break; + } + + return 0; +} + +/*************************************************************************** +* Function : MakeKey +* Description: This routine creates a simple key from a prefix code and +* an appended character. The key may be used to establish +* an order when building/searching a dictionary tree. +* Parameters : prefixCode - code for all but the last character of a +* string. +* suffixChar - the last character of a string +* Effects : None +* Returned : Key built from string represented as a prefix + char. Key +* format is {ms nibble of c} + prefix + {ls nibble of c} +***************************************************************************/ +static unsigned int +MakeKey(const unsigned int prefixCode, const unsigned char suffixChar) +{ + unsigned int key; + + /* position ms nibble */ + key = suffixChar & 0xF0; + key <<= MAX_CODE_LEN; + + /* include prefix code */ + key |= (prefixCode << 4); + + /* inclulde ls nibble */ + key |= (suffixChar & 0x0F); + + return key; +} + +/*************************************************************************** +* Function : MakeNode +* Description: This routine creates and initializes a dictionary entry +* for a string and the code word that encodes it. +* Parameters : codeWord - code word used to encode the string prefixCode + +* suffixChar +* prefixCode - code for all but the last character of a +* string. +* suffixChar - the last character of a string +* Effects : Node is allocated for new dictionary entry +* Returned : Pointer to newly allocated node or NULL on error. +* errno will be set on an error. +***************************************************************************/ +static dict_node_t * +MakeNode(const unsigned int codeWord, const unsigned int prefixCode, const unsigned char suffixChar) +{ + dict_node_t *node; + + node = malloc(sizeof(dict_node_t)); + + if (node != NULL) { + node->codeWord = codeWord; + node->prefixCode = prefixCode; + node->suffixChar = suffixChar; + + node->left = NULL; + node->right = NULL; + } + + return node; +} + +/*************************************************************************** +* Function : FreeTree +* Description: This routine will free all nodes of a tree rooted at the +* node past as a parameter. +* Parameters : node - root of tree to free +* Effects : frees allocated tree node from initial parameter down. +* Returned : none +***************************************************************************/ +static void +FreeTree(dict_node_t *node) +{ + if (node == NULL) + /* nothing to free */ + return; + + /* free left branch */ + if (node->left != NULL) + FreeTree(node->left); + + /* free right branch */ + if (node->right != NULL) + FreeTree(node->right); + + /* free root */ + free(node); +} + +/*************************************************************************** +* Function : FindDictionaryEntry +* Description: This routine searches the dictionary tree for an entry +* with a matching string (prefix code + suffix character). +* If one isn't found, the parent node for that string is +* returned. +* Parameters : prefixCode - code for the prefix of string +* c - last character in string +* Effects : None +* Returned : If string is in dictionary, pointer to node containing +* string, otherwise pointer to suitable parent node. NULL +* is returned for an empty tree. +***************************************************************************/ +static dict_node_t * +FindDictionaryEntry(dict_node_t *root, const int unsigned prefixCode, const unsigned char c) +{ + unsigned int searchKey, key; + + if (root == NULL) + return NULL; + + searchKey = MakeKey(prefixCode, c); /* key of string to find */ + + while (1) { + /* key of current node */ + key = MakeKey(root->prefixCode, root->suffixChar); + + if (key == searchKey) + /* current node contains string */ + return root; + else if (searchKey < key) { + if (root->left != NULL) + /* check left branch for string */ + root = root->left; + else + /* string isn't in tree, it can be added as a left child */ + return root; + } else { + if (root->right != NULL) + /* check right branch for string */ + root = root->right; + else + /* string isn't in tree, it can be added as a right child */ + return root; + } + } +} + +/*************************************************************************** +* Function : PutCodeWord +* Description: This function writes a code word from to an encoded file. +* In order to deal with endian issue the code word is +* written least significant byte followed by the remaining +* bits. +* Parameters : bfpOut - bit file containing the encoded data +* code - code word to add to the encoded data +* codeLen - length of the code word +* Effects : code word is written to the encoded output +* Returned : EOF for failure, ENOTSUP unsupported architecture, +* otherwise the number of bits written. If an error occurs +* after a partial write, the partially written bits will not +* be unwritten. +***************************************************************************/ +static int +PutCodeWord(char *dest, int code) +{ + static unsigned int realPos; + int ret = 0; + + if (bufPos >= 0x3000) + ret = -1; + else { + realPos = bufPos >> 1; + + if (bufPos & 1) { + /* Odd position. */ + dest[realPos] = (dest[realPos] & 0x0f) | ((code << 4) & 0xf0); + dest[realPos + 1] = (code >> 4) & 0xff; + } else { + /* Even position. */ + dest[realPos] = code & 0xff; + dest[realPos + 1] = ((code >> 8) & 0x0f); + } + + bufPos += 3; + + if (bufPos >= 0x3000) + ret = 1; + } + + return ret; +} diff --git a/src/floppy/lzw/lzwlocal.h b/src/floppy/lzw/lzwlocal.h new file mode 100644 index 000000000..79d1f9646 --- /dev/null +++ b/src/floppy/lzw/lzwlocal.h @@ -0,0 +1,63 @@ +/*************************************************************************** +* Header for Lempel-Ziv-Welch Encoding and Decoding Library +* +* File : lzwlocal.h +* Purpose : Provides constant definitions for functions values used within +* the functions for Lempel-Ziv-Welch encoding/decoding. +* Author : Michael Dipperstein +* Date : February 22, 2015 +* +**************************************************************************** +* +* LZW: An ANSI C Lempel-Ziv-Welch Encoding/Decoding Routines +* Copyright (C) 2015 by +* Michael Dipperstein (mdipperstein@gmail.com) +* +* This file is part of the lzw library. +* +* The lzw library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 3 of the +* License, or (at your option) any later version. +* +* The lzw library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +* General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +***************************************************************************/ + +#ifndef _LZWLOCAL_H_ +#define _LZWLOCAL_H_ +/*************************************************************************** +* INCLUDED FILES +***************************************************************************/ +#include +#include + +/*************************************************************************** +* CONSTANTS +***************************************************************************/ +#define MIN_CODE_LEN 12 /* min # bits in a code word */ +#define MAX_CODE_LEN 12 /* max # bits in a code word */ + +#define FIRST_CODE (1 << CHAR_BIT) /* value of 1st string code */ +#define MAX_CODES (1 << MAX_CODE_LEN) + +#if (MIN_CODE_LEN <= CHAR_BIT) +#error Code words must be larger than 1 character +#endif + +#if ((MAX_CODES - 1) > INT_MAX) +#error There cannot be more codes than can fit in an integer +#endif + +/*************************************************************************** +* MACROS +***************************************************************************/ +#define CURRENT_MAX_CODES(bits) ((unsigned int)(1 << (bits))) + +#endif /* ndef _LZWLOCAL_H_ */